第一个规范的问题在于,Python无法告诉您__getitem__
,在处my_obj.a.b.c.d
,您是否下一步将继续沿着不存在的树进行操作,在这种情况下,它需要使用一种__getitem__
方法返回一个对象,因此您不会被AttributeError
抛出,或者如果您想要一个值,则需要返回None
。
我认为,在每种情况下,您都应该期望它抛出KeyError
而不是返回None
。原因是您无法分辨是否None
表示“无钥匙”或“实际存储None
在该位置的人”。对于此行为,您所需要做的就是takedotdictify
,removemarker
和替换__getitem__
为:
def __getitem__(self, key):
return self[key]
因为您真正想要的是dict
with__getattr__
和__setattr__
。
可能有一种方法可以__getitem__
完全删除并说一些类似的内容__getattr__ = dict.__getitem__
,但是我认为这可能是过度优化的问题,如果您以后决定要__getitem__
像dotdictify
最初那样创建树,那么这将是一个问题,在这种情况下,您会更改为:
def __getitem__(self, key):
if key not in self:
dict.__setitem__(self, key, dotdictify())
return dict.__getitem__(self, key)
我不喜欢marker
原来的生意dotdictify
。
第二个规范(overrideget()
和set()
)是,法线dict
的get()
操作与您描述的不同,甚至不包含set
(尽管其setdefault()
操作与操作相反get()
)。人们期望get
采用两个参数,如果找不到密钥,则第二个参数为默认值。
如果要扩展__getitem__
并__setitem__
处理点键符号,则需要修改doctictify
为:
class dotdictify(dict):
def __init__(self, value=None):
if value is None:
pass
elif @R_403_937@(value, dict):
for key in value:
self.__setitem__(key, value[key])
else:
raise TypeError, 'expected dict'
def __setitem__(self, key, value):
if '.' in key:
myKey, restOfKey = key.split('.', 1)
target = self.setdefault(myKey, dotdictify())
if not @R_403_937@(target, dotdictify):
raise KeyError, 'cannot set "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
target[restOfKey] = value
else:
if @R_403_937@(value, dict) and not @R_403_937@(value, dotdictify):
value = dotdictify(value)
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if '.' not in key:
return dict.__getitem__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not @R_403_937@(target, dotdictify):
raise KeyError, 'cannot get "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
return target[restOfKey]
def __contains__(self, key):
if '.' not in key:
return dict.__contains__(self, key)
myKey, restOfKey = key.split('.', 1)
target = dict.__getitem__(self, myKey)
if not @R_403_937@(target, dotdictify):
return False
return restOfKey in target
def setdefault(self, key, default):
if key not in self:
self[key] = default
return self[key]
__setattr__ = __setitem__
__getattr__ = __getitem__
测试代码:
>>> life = dotdictify({'bigBang': {'stars': {'planets': {}}}})
>>> life.bigBang.stars.planets
{}
>>> life.bigBang.stars.planets.earth = { 'singleCellLife' : {} }
>>> life.bigBang.stars.planets
{'earth': {'singleCellLife': {}}}
>>> life['bigBang.stars.planets.mars.landers.vikings'] = 2
>>> life.bigBang.stars.planets.mars.landers.vikings
2
>>> 'landers.vikings' in life.bigBang.stars.planets.mars
True
>>> life.get('bigBang.stars.planets.mars.landers.spirit', True)
True
>>> life.setdefault('bigBang.stars.planets.mars.landers.opportunity', True)
True
>>> 'landers.opportunity' in life.bigBang.stars.planets.mars
True
>>> life.bigBang.stars.planets.mars
{'landers': {'opportunity': True, 'vikings': 2}}