属性树
第一个规范的问题是,Python无法在__getitem__中判断,如果在my_obj.a.b.c.d时,下一步将继续深入到不存在的树中,在这种情况下,它需要返回一个带有__getitem__方法的对象,这样就不会向您抛出AttributeError,或者如果您想要一个值,在这种情况下,它需要返回None。
我认为在上面的每一种情况下,您应该期望它抛出一个KeyError,而不是返回None。原因是你无法判断None是否意味着“没有密钥”或者“有人实际存储了None在那个位置”。对于此行为,您只需采取dotdictify,删除marker,并将__getitem__替换为:def __getitem__(self, key):
return self[key]
因为你真正想要的是一个带__getattr__和__setattr__的dict。
可能有一种方法可以完全删除__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业务。
路径支持
第二个规范(覆盖get()和set())是一个普通的dict有一个get(),它的操作与您描述的不同,甚至没有一个set(尽管它有一个setdefault(),这是get()的逆操作)。人们希望get接受两个参数,如果找不到密钥,则第二个参数是默认值。
如果要扩展__getitem__和__setitem__来处理点键表示法,则需要将doctictify修改为:class dotdictify(dict):
def __init__(self, value=None):
if value is None:
pass
elif isinstance(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 isinstance(target, dotdictify):
raise KeyError, 'cannot set "%s" in "%s" (%s)' % (restOfKey, myKey, repr(target))
target[restOfKey] = value
else:
if isinstance(value, dict) and not isinstance(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 isinstance(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 isinstance(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}}