How To Define A Super Powerful Class-style Dict Object?
Solution 1:
Subclass collections.defaultdict()
:
from collections import defaultdict, Mapping
class default_attribute_dict(defaultdict):
def __init__(self, *args, **kwargs):
super(default_attribute_dict, self).__init__(*args, **kwargs)
self.__dict__ = self
def __getattr__(self, name):
# trigger default
return self[name]
@classmethod
def from_dictionaries(cls, d, default=lambda: None):
cdic = cls(default)
for key, value in d.iteritems():
if isinstance(value, Mapping):
value = cls.from_dictionaries(value, default=default)
cdic[key] = value
return cdic
This will not automatically create nested instances of itself; you'll need to loop over the input dictionary and create nested objects yourself.
But it does offer attribute access and default values:
>>> cdic = default_attribute_dict(lambda: 100)
>>> cdic.hundred
100
>>> cdic['ten'] = 10
>>> cdic.ten
10
>>> cdic['ten']
10
To build your tree from an existing dictionary, use the from_dictionaries()
class method:
>>> cdic = default_attribute_dict.from_dictionaries(dic, default=lambda: 100)
>>> cdic
defaultdict(<function <lambda> at 0x109998848>, {'one': 1, 'three': 3, 'two': defaultdict(<function <lambda> at 0x109998848>, {'four': 4, 'five': defaultdict(<function <lambda> at 0x109998848>, {'seven': 7, 'six': 6})})})
>>> cdic.two.four
4
Note that keys on the dictionary can mask methods; keep that in mind when inserting keys that match dictionary methods:
>>> cdic = default_attribute_dict.from_dictionaries(dic, default=lambda: 100)
>>> cdic.keys
<built-in method keys of default_attribute_dict object at 0x7fdd0bcc9ac0>
>>> cdic['keys']
100
>>> cdic.keys
100
>>> cdic.keys()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
Solution 2:
If you want to be able to do
cdic.two = 2
You should also override __setattr__
def __setattr__(self, name, val):
self[name] = val
Solution 3:
Here's a variation of Martijn Pieters' answer which is a variation of this answer (which itself is a variation of this, which is based on a blog entry by James Robert). However, unlike the Martijn's it will automatically create nested instances of itself. A custom__repr__()
has also been added.
from collections import defaultdict, Mapping
class classdict(defaultdict):
def __init__(self, *args, **kwargs):
super(classdict, self).__init__(*args, **kwargs)
self.__dict__ = self
def __getattr__(self, name):
return self[name] # trigger default
def __missing__(self, key):
default = (None if self.default_factory is None else
self.default_factory())
self[key] = classdict.from_dict({key: default},
default=self.default_factory)
return self[key]
def __repr__(self):
return '{}({}, default={})'.format(self.__class__.__name__,
dict(self.__dict__), # no recursion
self.default_factory)
@classmethod
def from_dict(cls, d, default=lambda: None):
cdic = cls(default)
for key, value in d.iteritems():
cdic[key] = (value if not isinstance(value, Mapping)
else cls.from_dict(value, default=default))
return cdic
if __name__ == '__main__':
dic={'one': 1,
'two': {'four': 4,
'five': {'six': 6,
'seven': 7}
},
'three': 3
}
cdic = classdict.from_dict(dic, default=lambda: 100)
print 'cdic.one:', cdic.one
print 'cdic.two:', cdic.two
print 'cdic.two.five.six:', cdic.two.five.six
print "cdic['two']['five']['six']:", cdic['two']['five']['six']
print "cdic['two']['five']['six'] = 7"
cdic['two']['five']['six'] = 7
print 'cdic.two.five.six:', cdic.two.five.six
print 'cdic.two.five.six = 8'
cdic.two.five.six = 8
print "cdic['two']['five']['six']:", cdic['two']['five']['six']
print "cdic['two']['five']['eight'] = 8"
cdic['two']['five']['eight'] = 8
print 'cdic.two.five.eight:', cdic.two.five.eight
print 'cdic.two.five.nine = 9'
cdic.two.five.nine = 9
print "cdic['two']['five']['nine']:", cdic['two']['five']['nine']
print "cdic['ten']:", cdic['ten']
print 'cdic.ten:', cdic.ten
print 'cdic.eleven:', cdic.eleven
print "cdic['eleven']:", cdic['eleven']
print "final cdic:\n ", cdic
Solution 4:
After studying all the answers,I wrote the solution by myself.
It meets my needs well and no "import xxx" is required,also kind to print.
(I think mine is better than this one I have planned to share yesterday)
my solution:
class DictObj(dict):
default=None
def __init__(self, dic, default):
DictObj.default = default
for key,value in dic.items():
if isinstance(value,dict):
self.__setattr__(key, DictObj(value,DictObj.default))
else:
self.__setattr__(key, value)
def __getitem__(self, key):
return self.__getattr__(key )
def __setitem__(self, key, value):
self.__setattr__(key,value)
def __getattr__( self ,key ):
if key not in self:
self.__setattr__(key,DictObj.default)
return self.__dict__[key]
def __setattr__( self ,key ,value ):
self.__dict__[key]=value
dict.__setitem__(self, key, value)
def printself(self):
print self
dic={'one':1,
'two':{
'four':4,
'five':{
'six':6,
'seven':7,}},
'three':3}
cdic=DictObj(dic,100)
print '-------------------the start state of cdic-------------------------------------------'
print cdic
print '-------------------query in two ways-------------------------------------------'
print 'cdic.two.five-->',cdic.two.five
print "cdic['two']['five']-->",cdic['two']['five']
print 'cdic.two.five.six-->',cdic.two.five.six
print "cdic['two']['five']['six']-->",cdic['two']['five']['six']
print '-------------------update in two ways-------------------------------------------'
cdic['two']['five']['six']=7
print "cdic['two']['five']['six']=7"
print "cdic.two.five.six-->",cdic.two.five.six
cdic.two.five.six=6
print "cdic.two.five.six=6"
print "cdic['two']['five']['six']-->",cdic['two']['five']['six']
print '-------------------add in two ways-------------------------------------------'
cdic['two']['five']['eight']=8
print "cdic['two']['five']['eight']=8"
print "cdic.two.five.eight-->",cdic.two.five.eight
cdic.two.five.nine=9
print "cdic.two.five.nine=9"
print "cdic['two']['five']['nine']-->",cdic['two']['five']['nine']
print '-------------------query default in two ways-------------------------------------------'
print "cdic['ten']-->",cdic['ten']
print "cdic.eleven-->",cdic.eleven
print "cdic.two.five.twelve-->",cdic.two.five.twelve
print '-------------------the final state of cdic-------------------------------------------'
print cdic,'\n'
cdic.printself()
the result:
-------------------the start state of cdic-------------------------------------------
{'one': 1, 'three': 3, 'two': {'four': 4, 'five': {'seven': 7, 'six': 6}}}
-------------------query in two ways-------------------------------------------
cdic.two.five--> {'seven': 7, 'six': 6}
cdic['two']['five']--> {'seven': 7, 'six': 6}
cdic.two.five.six--> 6
cdic['two']['five']['six']--> 6
-------------------update in two ways-------------------------------------------
cdic['two']['five']['six']=7
cdic.two.five.six--> 7
cdic.two.five.six=6
cdic['two']['five']['six']--> 6
-------------------add in two ways-------------------------------------------
cdic['two']['five']['eight']=8
cdic.two.five.eight--> 8
cdic.two.five.nine=9
cdic['two']['five']['nine']--> 9
-------------------query default in two ways-------------------------------------------
cdic['ten']--> 100
cdic.eleven--> 100
cdic.two.five.twelve--> 100
-------------------the final state of cdic-------------------------------------------
{'eleven': 100, 'one': 1, 'three': 3, 'ten': 100, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 6, 'eight': 8, 'twelve': 100}}}
{'eleven': 100, 'one': 1, 'three': 3, 'ten': 100, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 6, 'eight': 8, 'twelve': 100}}}
Post a Comment for "How To Define A Super Powerful Class-style Dict Object?"