idict是dict的子类,它的键值和属性是同步的,并且有强大的默认值机制.
例如,假设x是idict的一个实例,且x['a']['b']=12,则有x.a.b=12.反之亦然;
假设'c'不在x的键集合,那么尝试访问x['c']或者x.c,均会直接初始化默认值.
class idict(dict): "super dict class, attributes and keys are equal" def __init__(self,d={},dft=None): idict.dft=dft for k,v in d.items(): if isinstance(v,dict): self.__setitem__(k,idict(v,idict.dft)) else: self.__setitem__(k,v) def __setitem__(self,k,v): dict.__setitem__(self,k,v) dict.__setattr__(self,k,v) def __missing__(self,k): self.__setitem__(k,idict.dft) return idict.dft __setattr__=__setitem__ __getattr__=__missing__
- 字典访问d['k']和属性访问d.k有着微妙的关系.
对于常规访问,字典端对应__getitem__,属性端对应__getattribute__
对于缺失访问,字典端对应__missing__,属性端对应__getattr__
对于添加或更新值,字典端对应__setitem__,属性端对应__setattr__
- __init__中定义一个类属性idict.dft=dft是非常有必要的.(为什么不self.dft=dft?)
- __init__能调用实例的__setitem__方法,也能递归调用父类idict.
- 子类的__init__未必总是需要调用父类的__init__.此例中,通过递归idict(v,idict.dft)初始化实例属性,非常精妙.
- 重写特殊方法的最简单的工具是直接调用父类的同类方法.例如在定义idict的__setitem__方法时,用到了dict.__setitem__
idict有什么实际的好处?
首先能够设置默认值,这样就能减少一些判断.这相当于dict.setdefault(k,v),但是idict[k]不是优雅许多吗?
其次属性形式的控制风格,便于写代码(虽然看起来有些无聊).例如idict['a']['b']['c']=2,就没有idict.a.b.c=2优雅.
总之,通过这次定制dict类,我发现Python面向对象所蕴含的强大力量.
测试代码:
if __name__=='__main__': dic={'one':1, 'two':{ 'four':4, 'five':{ 'six':6, 'seven':7,}}, 'three':3} cdic=idict(dic,'default') 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 new one 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 and set default in two ways-------------------------------------------') print("cdic['ten']-->",cdic['ten']) print("cdic.eleven-->",cdic.eleven) print("cdic.two.five.twelve-->",cdic.two.five.twelve) print("cdic['two']['five']['thirteen']-->",cdic['two']['five']['thirteen']) print('-------------------the final state of cdic-------------------------------------------') print('dict view--print(cdic):') print(cdic) print(' attributes view--print(cdic.__dict__):') print(cdic.__dict__) print() def show(d): for k,v in d.items(): if isinstance(v,(str,int)): yield '%s->%s'%(k,v) else: for i in show(v): yield '%s.%s'%(k,i) for i in show(cdic): print('cdic.'+i)
测试结果:
>>> -------------------the start state of cdic------------------------------------------- {'two': {'five': {'seven': 7, 'six': 6}, 'four': 4}, 'one': 1, 'three': 3} -------------------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 new one in two ways------------------------------------------- cdic['two']['five']['eight']=8 cdic.two.five.eight--> 8 cdic.two.five.nine=9 cdic['two']['five']['nine']--> 9 -------------------query and set default in two ways------------------------------------------- cdic['ten']--> default cdic.eleven--> default cdic.two.five.twelve--> default cdic['two']['five']['thirteen']--> default -------------------the final state of cdic------------------------------------------- dict view--print(cdic): {'two': {'five': {'eight': 8, 'nine': 9, 'twelve': 'default', 'seven': 7, 'thirteen': 'default', 'six': 6}, 'four': 4}, 'ten': 'default', 'one': 1, 'eleven': 'default', 'three': 3} attributes view--print(cdic.__dict__): {'two': {'five': {'eight': 8, 'nine': 9, 'twelve': 'default', 'seven': 7, 'thirteen': 'default', 'six': 6}, 'four': 4}, 'ten': 'default', 'one': 1, 'eleven': 'default', 'three': 3} cdic.two.five.eight->8 cdic.two.five.nine->9 cdic.two.five.twelve->default cdic.two.five.seven->7 cdic.two.five.thirteen->default cdic.two.five.six->6 cdic.two.four->4 cdic.ten->default cdic.one->1 cdic.eleven->default cdic.three->3