I have nested dictionaries using a lambda function, since the last level of them is a custom dataclass. I want to write them to a pickle file, but I can't do it because of the lambda functions in the attachment. Is there a way to solve this problem?
#dataclass
class data_storage():
def __init__(self):
self.word_cloud = defaultdict(int)
self.word_cloud[tuple(['total'])] = 0
self.history = [set(), set(), set(), 0]
a = defaultdict(lambda: defaultdict(data_storage))
I tried the recursive method of setting dictionaries, but I could not make the desired level in it a custom class.
def ddict():
return defaultdict(ddict)
Related
What am I trying to solve:
I want to create an object that is based on a dictionary.
The object should contain some additional methods that are out of the scope of this question.
There dictionary passed must contain a known set of keys.
What obviously will work is:
class DictBasedObject(object):
def __init__(self, dictionary: {})
if 'known_key_1' in dictionary:
self.known_key_1 = dictionary['known_key_1']
if 'known_key_2' in dictionary:
self.known_key_2 = dictionary['known_key_2']
...
however this way is rather cumbersome.
How I am trying to solve:
I would like to pass the dictionary as argument to a method, but with possibly specifying of the dictionary key-names/value-types, like:
class DictBasedObject(object):
def __init__(self, dictionary: {'known_key_1': str,
'known_key_2': int,
...})
self.known_key_1 = dictionary['known_key_1']
self.known_key_2 = dictionary['known_key_2']
...
Obviously not much better, but at least one step towards 'more elegant'.
Is there an elegant and pythonic manner to solve this?
You can either use the setattr function:
for k, v in dictionary.items():
setattr(self, k) = v
or simply update the __dict__ attribute of the object:
self.__dict__.update(dictionary)
I have a situation where I need to create a dictionary that keeps track of global order of the values. I haven't been able to find a good way for the class itself to have an incrementing counter that's also tracked by the value.
Here's what I've written in the meanwhile to get around this:
from collections import defaultdict
class NotMyDict(object):
""" defaultdict(list) that tracks order globally across the dict.
Will function as a normal defaultdict(list) unless you modify the
'ordered' attribute and set it to a non-false evaluating value. This
"""
ordered = False
_data = defaultdict(NotMyDictList)
_next_index = 0
class NotMyDictList(list):
def append(self, value):
def __repr__(self):
if self.ordered:
return repr(self._data)
else:
temp = defaultdict(list)
for key in self._data:
for value in self._data[key]:
temp[key].append(value[0])
return repr(temp)
def __getitem__(self, key):
if self.ordered:
return self._data[key]
else:
return [val[0] for val in self._data[key]]
def add_value_to_key(self, key, value):
self._data[key].append((value, self._next_index))
self._next_index += 1
So I can use this like a normal dictionary for pulling values. I could have instantiated a list if the key didn't exist, but defaultdict was simple and easy.
Here's an example of the use:
test = NotMyDict()
test.add_value_to_key('test', 'hi')
test.add_value_to_key('test', 'there')
test.add_value_to_key('test', 'buddy')
test['test']
Result:
['hi', 'there', 'buddy']
test.ordered = True
test['test']
Result:
[('hi', 0), ('there', 1), ('buddy', 2)]
Now - the example of use isn't super important, but the functionality that I can't seem to figure out, is instead of using the .add_value_to_key(), I want to be able to use a normal defaultdict(list) convention of:
dict[key].append()
and still have it track the index. Do I need to pass global object locations with id() and reference those objects at a memory level, or is there a way I just don't understand to have a "class global" that's accessible by it's members?
I had also tried to use nested classes, but the nested class didn't have access to the parent class's globals, so I'd have to:
Make a list-like class that references the parent class attribute somehow (Maybe with id() and direct memory location reference?)
modify/make it's append() function so that it also updates the parent class global counter, and tracks the value with this counter as a metadata field.
I really just can't seem to wrap my head around how to create this object/class in a way that let's me use the same functionality of a defaultdict(list) where I can index/append directly AND have it track the global index order of that new value.
dict[key].append(value)
Help would be appreciated - I sunk three hours into trying different solutions before I scrapped it and went with the "just use this method to append" for now.
The error comes from publishDB = defaultdict(defaultdict({})) I want to make a database like {subject1:{student_id:{assignemt1:marks, assignment2:marks,finals:marks}} , {student_id:{assignemt1:marks, assignment2:marks,finals:marks}}, subject2:{student_id:{assignemt1:marks, assignment2:marks,finals:marks}} , {student_id:{assignemt1:marks, assignment2:marks,finals:marks}}}. I was trying to populate it as DB[math][10001] = a dict and later read out as d = DB[math][10001]. Since, I am on my office computer I can not try different module.
Am I on right track to do so?
Such a nested dict structure can be achieved using a recursive defaultdict "tree":
def tree():
return defaultdict(tree)
publishDB = tree()
At each level, the defaultdicts are instantiated with tree which is a zero-argument callable, as required.
Then you can simply assign marks:
publishDB[subject][student][assignment] = mark
defaultdict() requires that its first argument be callable: it must be a class that you want an instance of, or a function that returns an instance.
defaultdict({}) has an empty dictionary, which is not callable.
You likely want defaultdict(dict), as dict is a class that returns a dictionary when instantiated (called).
But that still doesn't solve the problem... just moves it to a different level. The outer defaultdict(...) in defaultdict(defaultdict(dict)) has the exact same issue because defaultdict(dict) isn't callable.
You can use a lambda expression to solve this, creating a one-line function that, when called, creates a defaultdict(dict):
defaultdict(lambda: defaultdict(dict))
You could also use the lambda at the lower level if you wanted:
defaultdict(lambda: defaultdict(lambda: {}))
I am dealing with classes whose attributes are sometimes list whose elements can be dictionaries or further nested objects with attributes etc. I would like to perform some slicing that with my grasp of python is only doable with what feels profoundly un-Pythonic.
My minimal code looks like this:
class X(object):
def __init__(self):
self.a = []
x=X()
x.a.append({'key1':'v1'})
x.a.append({'key1':'v2'})
x.a.append({'key1':'v3'})
# this works as desired
x.a[0]['key1'] # 'v1'
I would like to perform an access to a key in the nested dictionary but make that call for all elements of the list containing that dictionary. The standard python way of doing this would be a list comprehension a la:
[v['key1'] for v in x.a]
However, my minimal example doesn't quite convey the full extent of nesting in my real-world scenario: The attribute list a in class X might contain objects, whose attributes are objects, whose attributes are dictionaries whose keys I want to select on while iterating over the outer list.
# I would like something like
useful_list = x.a[:]['key1'] # TypeError: list indices must be integers, not str
# or even better
cool_list = where(x.a[:]['key1'] == 'v2') # same TypeError
If I start list comprehending for every interesting key it quickly doesn't look all that Pythonic. Is there a nice way of doing this or do I have to code 'getter' methods for all conceivable pairings of lists and dictionary keys?
UPDATE:
I have been reading about overloading lists. Apparently one can mess with the getitem method which is used for indeces for lists and keys for dict. Maybe a custom class that iterates over list members. This is starting to sound contrived...
So, you want to create an hierarchical structure, with an operation which means
different things for different types, and is defined recursively.
Polymorphism to the rescue.
You could override __getitem__ instead of my get_items below, but in your case it might be better to define a non-builtin operation to avoid risking ambiguity. It's up to you really.
class ItemsInterface(object):
def get_items(self, key):
raise NotImplementedError
class DictItems(ItemsInterface, dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
def get_items(self, key):
res = self[key]
# apply recursively
try:
res = res.get_items(key)
except AttributeError:
pass
return res
class ListItems(ItemsInterface, list):
def __init__(self, *args, **kwargs):
list.__init__(self, *args, **kwargs)
def get_items(self, key):
return [ x.get_items(key) for x in self ]
x = ListItems()
x.append(DictItems({'key1':'v1'}))
x.append(DictItems({'key1':'v2'}))
x.append(DictItems({'key1':'v3'}))
y = DictItems({'key1':'v999'})
x.append(ListItems([ y ]))
x.get_items('key1')
=> ['v1', 'v2', 'v3', ['v999']]
Of course, this solution might not be exactly what you need (you didn't explain what it should do if the key is missing, etc.)
but you can easily modify it to suit your needs.
This solution also supports ListItems as values of the DictItems. the get_items operation is applied recursively.
I am trying to write a wrapper object around the dictionary object in python like so
class ScoredList():
def __init__(self,dct={}):
self.dct = dct
list = ScoredList()
list.dct.update({1,2})
list2 = ScoredList()
list.dct.update({"hello","world"})
print list1.dct, list2.dct # they are the same but should not be!
It seems like I am unable to create a new ScoredList object, or rather, every scored list object shares the same underlying dictionary. Why is this?
class ScoredList2():
def __init__(self):
self.dct = {}
The above code for ScoredList2 works fine. But I want know how to overload the constructor properly in python.
A dictionary is a mutable object. In Python, default values are parsed when the function is created, meaning the same empty dictionary is assigned to every new object.
To solve this, simply do something like:
class ScoredList():
def __init__(self, dct=None):
self.dct = dct if dct is not None else {}