Python 通过继承实现标准对象的子类

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('\nattributes 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

 

posted @ 2013-11-17 23:27  LisPythoniC  阅读(1315)  评论(0编辑  收藏  举报