Is it possible to swap two keys of an ordered dictionary? - python-3.x

Note: I am using python 3.8.2, so dictionaries are considered ordered
I was creating a binary tree, and I used dictionaries to model the tree.
For example:
{1:[2, 3], 2:[4, 5], 3:[], 4:[], 5:[]}
In the example, the tree above would look like this:
1
/ \
2 3
/ \
4 5
I was trying to simulate the ‘rising’ of some nodes, and keep the order of the dict as well.
I know that using myDict[key1], myDict[key2] = myDict[key2], myDict[key1] won’t work, as the places of the values change, not the keys.
I was also thinking of using .popitem() to remove the last value until I’m either at key1 or key2, and the keep going until I get to the other key, but this seems kinda hard. Is there any other way to do this?

Dictionaries, although they now keep the insertion order, are not arbitrarily orderable otherwise. If you want really to use the dict order information to build your tree, I think the only reliable way would be to build a fresh dictionary, copying the contents of the original one, for each of these swap operations.
A more reasonable approach, if you want an arbitrarily ordered dictionary would be to inherit from collections.abc.MutableMappingand kep track of your data inside that object, using a dictionary and some other data structure, such as a list.
It may sound complicated, but it may be simpler than you think:
from collections.abc import MutableMapping
class SuperOrdered(MutableMapping):
def __init__(self):
self.data = {}
self.order = []
def __setitem__(self, key, value):
if key not in self.data:
self.order.append(key)
self.data[key] = value
def __getitem__(self, key):
return self.data[key]
def __delitem__(self, key):
del self.data[key]
self.order.remove(key)
def __len__(self):
return len(self.data)
def __iter__(self):
yield from iter(self.order)
def replace_key(self, oldkey, newkey, value):
if newkey in self.data:
del self[newkey]
position = self.order.index(oldkey)
self.order[position] = newkey
self.data[newkey] = value
def __repr__(self):
return f"{self.__class__.__name__}({{{', '.join(repr(key) + ':' + repr(self.data[key]) for key in self)}}})"
And voilá - the mapping + the "replace_key" method should be enough for you to build your tree as you are thinking about it.
This is the class above in the interactive prompt:
In [18]: aa = SuperOrdered()
In [19]: aa["a"] = 1;aa["b"] = 2;aa["c"] = 3
In [20]: aa
Out[20]: SuperOrdered({'a':1, 'b':2, 'c':3})
In [21]: aa.replace_key("a", "d", 4)
In [22]: aa
Out[22]: SuperOrdered({'d':4, 'b':2, 'c':3})
Apart from this answer, and out of topic: if you want to check a tree implementation that I hope is "production ready", I've published one as part of my extradict package (pip installable).
update: One might also inherit from collections.OrderedDict and add a replace_key method there. That code would have to deal with OrderedDict internals, but it would not be hard.
External links:
Github modification

Since you only want to swap two keys, we could start off with the following code:
>>> changedDict = {}
>>> for key, value in myDict.items():
... if key not in (key1, key2):
... changedDict[key] = value
And continue as follows:
... elif key == key1:
... changedDict[key] = myDict[key2]
... else:
... changedDict[key] = myDict[key1]

Related

How to convert python dict to DictRow object

Hi I am writing unittest using pytest. But I am not able to mock few db functions. We are using psycopg2 for db connections and executions. Response of query returned from psycopg2 is of the type DictRow which can be accessed either by key or by index.
Ex:
response = ['prajwal', '23', 'engineer'] #Response of a query "select name, age , job from users"
>>>response[0]
'prajwal'
>>>response['name']
'prajwal'
I want to know is there any way by which we can covert dict/list to above mentioned type.
Looking at the source for psycopg2, creating a DictRow requires passing in a DictCursor object. However the only thing it uses from DictCursor appears to be an index and description attribute.
# found in lib\site-packages\psycopg2.extras.py
class DictRow(list):
"""A row object that allow by-column-name access to data."""
__slots__ = ('_index',)
def __init__(self, cursor):
self._index = cursor.index
self[:] = [None] * len(cursor.description)
The index looks like a dict with a mapping a key to an index. e.g.response['name'] = 0
The description looks like your dict that you want to convert.
If you're feeling hacky you could take advantage of duck typing and pretend you're passing in a cursor when you're just satisfying the requirements.
The only caveat is after we instantiate the DictRow, we need to populate it. Our fake cursor hack will take care of the rest.
from psycopg2.extras import DictRow
class DictRowHack:
def __init__(self, my_dict):
# we need to set these 2 attributes so that
# it auto populates our indexes
self.index = {key: i for i, key in enumerate(my_dict)}
self.description = my_dict
def dictrow_from_dict(my_dict):
# this is just a little helper function
# so you don't always need to go through
# the steps to recreate a DictRow
fake_cursor = DictRowHack(my_dict)
my_dictrow = DictRow(fake_cursor)
for k, v in my_dict.items():
my_dictrow[k] = v
return my_dictrow
response = {'name': 'prajwal', 'age': '23', 'job': 'engineer'}
my_dictrow = dictrow_from_dict(response)
print(my_dictrow[1])
print(my_dictrow['name'])
print(type(my_dictrow))

Best way to model JSON data in python

This question may be opinion based, but I figured I'd give it shot.
I am attempting to create a variety of classes which gets its values from JSON data. The JSON data is not under my control so I have to parse the data and select the values I want. My current implementation subclasses UserDict from python3's collection module. However, I have had iterations where I have directly created attributes and set the values to the parsed data.
The reason I changed to using the UserDict is the ease of using the update function.
However, I feel odd calling the object and using MyClass['attribute'] rather than MyClass.attribute
Is there a more pythonic way to model this data?
I am not 100% convinced that this makes sense, but you could try this:
class MyClass (object):
def __init__(self, **kwargs):
for key in kwargs.keys():
setattr(self, key, kwargs[key])
my_json = {"a":1, "b":2, "c":3}
my_instance = MyClass(**my_json)
print (my_instance.a)
# 1
print (my_instance.b)
# 2
print (my_instance.c)
# 3
--- edit
in case you have nested data you could also try this:
class MyClass (object):
def __init__(self, **kwargs):
for key in kwargs.keys():
if isinstance(kwargs[key],dict):
setattr(self, key, MyClass(**kwargs[key]))
else:
setattr(self, key, kwargs[key])
my_json = {"a":1, "b":2, "c":{"d":3}}
my_instance = MyClass(**my_json)
print (my_instance.a)
# 1
print (my_instance.b)
# 2
print (my_instance.c.d)
# 3

Managed dict of list not updated in multiprocessing when using += operator

Consider the following python code:
from multiprocessing import Process, Manager
class MyClass():
def __init__(self, dic1, dic2):
self.dic1 = Manager().dict(dic1) # Create a managed dictionary
self.dic2 = Manager().dict(dic2) # Create a managed dictionary
process1 = Process(target=self.dictSumOverloaded, args=())
process2 = Process(target=self.dictSumElementWise, args=())
process1.start()
process1.join()
process2.start()
process2.join()
def dictSumOverloaded(self):
self.dic1['1'][0] += 1 # dic1 is not updated
def dictSumElementWise(self):
a = self.dic2['1']
self.dic2['1'] = [a[0]+1, a[1], a[2]] # dic2 is updated
def main():
dic1 = {'1': [1, 0, 0]}
dic2 = {'1': [1, 0, 0]}
result = MyClass(dic1, dic2)
print(result.dic1) # Failed
print(result.dic2) # Success
# Bypass multiprocessing environment
dic3 = {'1': [1, 0, 0]}
dic3['1'][0]+=1
print(dic3) # Success
if __name__ == '__main__':
main()
In this example, I create a managed dict containing a list as an attribute of MyClass. The goal is to increment some of the elements of this list in a multiprocessing environment, but some methods do not effectively modify the list.
Method 1: dictSumOverloaded
The overloaded operator += is used to increment an element of the list by 1 but the result does not persist. The dict is not updated.
Method 2: dictSumElementWise
This function creates a new list element wise, based on the old list and the values to add. Then the new list is assigned to the dict key. The dict is successfully modified.
Sanity check: outside the multiprocessing environment
dic3 is modified as expected when using += outside the multiprocessing environment.
Questions:
1) Why is += not modifying the list element in the multiprocessing environment?
2) Using the element wise method to update the list works but is cumbersome, any suggestion on making it cleaner/faster?
I believe the problem you are encountering is related to detection of a change in the dictionary dic1 by the anonymous Manager object that you create it with.
Changing the list itself with += operator does not change the reference to the list - it is the same list, just an element of it has changed (namely 0-th element of a list stored in the thread-safe dictionary dic1 under the key '1').
With dic2 the situation is different. With the following line:
self.dic2['1'] = [a[0]+1, a[1], a[2]]
You effectively update the value stored under the key '1'. The assigned value is a completely new list. It is made of elements of the list stored as the previous value under the same key but it is nevertheless a different list.
Such a change is detected by the Manager object and the reference in the process in which you check the value of dic2 is seamlessly updated so that you could read the correct value.
The main point here is the following:
the thread-safe collection (dict) does not propagate any changes to other processes (or threads) if there are no changes to keys, or values, or both. List is a reference type so the value (i.e. reference) does not change even if the list values change.

How to implement python dictionaries into code to do the same job as list functions

I need to be able to implement dictionaries into this code. Not all needs to be changed just were i can change it and it still does the same job.
In a test file I have a list of three strings (1, once),(2,twice).(2, twice).
I'm guessing the number will represent the value.
This code passes the tests but I am struggling to understand how I can use dictionaries to make it do the same job.
If any one can help it'll be grateful.
The current is:
The list items are in a test file elsewhere.
class Bag:
def __init__(self):
"""Create a new empty bag."""
self.items = []
def add(self, item):
"""Add one copy of item to the bag. Multiple copies are allowed."""
self.items.append(item)
def count(self, item):
"""Return the number of copies of item in the bag.
Return zero if the item doesn't occur in the bag.
"""
counter = 0
for an_item in self.items:
if an_item == item:
counter += 1
return counter
def clear(self, item):
"""Remove all copies of item from the bag.
Do nothing if the item doesn't occur in the bag.
"""
index = 0
while index < len(self.items):
if self.items[index] == item:
self.items.pop(index)
else:
index += 1
def size(self):
"""Return the total number of copies of all items in the bag."""
return len(self.items)
def ordered(self):
"""Return the items by decreasing number of copies.
Return a list of (count, item) pairs.
"""
result = set()
for item in self.items:
result.add((self.count(item), item))
return sorted(result, reverse=True)
I have been scratching my head over it for a while now. I can only use these also for dictionaries.
Items[key] = value
len(items)
dict()
items[key]
key in items
Del items[key]
Thank you
Start with the simplest possible problem. You have an empty bag:
self.items = {}
and now a caller is trying to add an item, with bag.add('twice').
Where shall we put the item?
Well, we're going to need some unique index.
Hmmm, different every time, different every time, what changes with each .add()?
Right, that's it, use the length!
n = len(self.items)
self.items[n] = new_item
So items[0] = 'twice'.
Now, does this still work after a 2nd call?
Yes. items[1] = 'twice'.
Following this approach you should be able to refactor the other methods to use the new scheme.
Use unit tests, or debug statements like print('after clear() items is: ', self.items), to help you figure out if the Right Thing happened.

Iterating through class variables in python

Please correct my code
PS - i'm fairly new to python
class Contact:
def __init__(self,cid, email):
self.cid=cid
self.email=email
def ind(contacts):
index={}
#Code here
return index
contacts = [Contact(1,'a'),
Contact(2,'b'),
Contact(3,'c'),
Contact(4,'a')]
print(ind(contacts))
Need the output to be like -
{'a':[1,4], 'b':2, 'c':3}
The following methods create list values like:
{'a':[1,4], 'b':[2], 'c':[3]}
I can't imagine why this wouldn't be fine, but I've added a method at the end that gets your specific output.
This doesn't maintain order of the emails:
def ind(contracts):
index={}
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
To maintain order (e.g. start with 'a'), add from collects import OrderedDict to the top of your file and then the method is:
def ind(contracts):
index = OrderedDict()
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
The printout of index will look different, but it acts the same as a normal dict object (just with ordering).
Exact output (with ordering):
def ind(contracts):
index = OrderedDict()
for contract in contracts:
if contract.email in index:
value = index[contract.email]
if not isinstance(value, list):
index[contract.email] = [value]
index[contract.email].append(contract.cid)
else:
index[contract.email] = contract.cid
return index

Resources