How To Define A Super Powerful Class-style Dict Object?
Solution 1:
Subclass collections.defaultdict()
:
from collections import defaultdict, Mapping
classdefault_attribute_dict(defaultdict):
def__init__(self, *args, **kwargs):
super(default_attribute_dict, self).__init__(*args, **kwargs)
self.__dict__ = self
def__getattr__(self, name):
# trigger defaultreturn self[name]
@classmethoddeffrom_dictionaries(cls, d, default=lambda: None):
cdic = cls(default)
for key, value in d.iteritems():
ifisinstance(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'objectisnotcallable
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
classclassdict(defaultdict):
def__init__(self, *args, **kwargs):
super(classdict, self).__init__(*args, **kwargs)
self.__dict__ = self
def__getattr__(self, name):
return self[name] # trigger defaultdef__missing__(self, key):
default = (Noneif self.default_factory isNoneelse
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)
@classmethoddeffrom_dict(cls, d, default=lambda: None):
cdic = cls(default)
for key, value in d.iteritems():
cdic[key] = (value ifnotisinstance(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'] = 7print'cdic.two.five.six:', cdic.two.five.six
print'cdic.two.five.six = 8'
cdic.two.five.six = 8print"cdic['two']['five']['six']:", cdic['two']['five']['six']
print"cdic['two']['five']['eight'] = 8"
cdic['two']['five']['eight'] = 8print'cdic.two.five.eight:', cdic.two.five.eight
print'cdic.two.five.nine = 9'
cdic.two.five.nine = 9print"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:
classDictObj(dict):
default=Nonedef__init__(self, dic, default):
DictObj.default = default
for key,value in dic.items():
ifisinstance(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 notin self:
self.__setattr__(key,DictObj.default)
return self.__dict__[key]
def__setattr__( self ,key ,value ):
self.__dict__[key]=value
dict.__setitem__(self, key, value)
defprintself(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']=7print"cdic['two']['five']['six']=7"print"cdic.two.five.six-->",cdic.two.five.six
cdic.two.five.six=6print"cdic.two.five.six=6"print"cdic['two']['five']['six']-->",cdic['two']['five']['six']
print'-------------------add in two ways-------------------------------------------'
cdic['two']['five']['eight']=8print"cdic['two']['five']['eight']=8"print"cdic.two.five.eight-->",cdic.two.five.eight
cdic.two.five.nine=9print"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?"