假设满足我需要的对象被称为classdic
,那么类classdic
的实例的功能是:
查询,更新,添加和删除数据都可以用类风格和dict风格方式实现(称为“两种方式”)。
当一个属性或一个键不存在时,classdic的实例可以自动构建它并使它等于实例和dict上的默认值,因此我们可以两种方式查询它(注意:不添加,只是查询)。
那么,我该如何实现这个类?
下面的示例显示了此类的实例如何工作:
dic={'one':1,
'two':{'four':4,'five':{'six':6,'seven':7}},
'three':3}
cdic=classdic(dic,default=100)
-------------------query in two ways-------------------------------------------
>>> cdic.one
1
>>> cdic.two
{'four':4,'five':{'six':6,'seven':7}}
>>> 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=8
>>> cdic['two']['five']['six']
8
-------------------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-------------------------------------------
>>> print cdic['ten']
100
>>> cdic.ten
100
>>> print cdic.eleven
100
>>> cdic['eleven']
100
-------------------the final state of cdic-------------------------------------------
>>> cdic
{'eleven': 100, 'three': 3, 'two': {'four': 4, 'five': {'nine': 9, 'seven': 7, 'six': 8, 'eight': 8}}, 'ten': 100, 'one': 1}
答案 0 :(得分:4)
子类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
这将不自动创建自己的嵌套实例;你需要循环输入字典并自己创建嵌套对象。
但它确实提供了属性访问和默认值:
>>> cdic = default_attribute_dict(lambda: 100)
>>> cdic.hundred
100
>>> cdic['ten'] = 10
>>> cdic.ten
10
>>> cdic['ten']
10
要从现有字典构建树,请使用from_dictionaries()
类方法:
>>> 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
请注意,字典上的键可以掩盖方法;插入与字典方法匹配的键时请记住这一点:
>>> 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
答案 1 :(得分:0)
如果你想能够做到
cdic.two = 2
您还应该覆盖 __ setattr __
def __setattr__(self, name, val):
self[name] = val
答案 2 :(得分:0)
以下是Martijn Pieters'answer的变体,它是此answer的变体(其本身是this的变体,基于blog entry by詹姆斯罗伯特)。但是,与Martijn不同,它将 自动创建自己的嵌套实例。还添加了自定义__repr__()
。
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
答案 3 :(得分:0)
在研究了所有答案后,我自己编写了解决方案。
它很好地满足了我的需求,并且不需要“import xxx”,也很适合打印。
(我认为我比昨天计划分享的this one更好)
我的解决方案:
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 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}}}