在语言级别支持AOP就是爽!不过这里面的规则超级复杂啊啊啊啊啊啊!!!记不住只好写下来。要理解这些,首先要明白Python中的type和class(new-style class已经和type统一了)自身也是在程序空间中可以被操纵的object。
主要手段有:
__get__和__set__(其实还有__delete__)
__getattr__(类似method missing)
__setattr__(虽然和上边那个长得像,但其实这俩很不一样,不放一起)
__getattribute__(和__setattr__一样都是终极大法)
1. __get__和__set__ 是定义在descriptor(http://docs.python.org/tut/node18.html)的class/type中的。
Any new-style object that defines the methods __get__(), __set__(), or __delete__(). When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, writing a.b looks up the object b in the class dictionary for a, but if b is a descriptor, the defined method gets called. Understanding descriptors is a key to a deep understanding of Python because they are the basis for many features including functions, methods, properties, class methods, static methods, and reference to super classes.
先说说怎么正确定义descriptor。下文说的class都是new-style的。那些descriptor的特殊方法(__get__, __set__, __delete__)必须定义为class/type object attribute,之后通过实例化得到这些方法的instance object,才被视作descriptor。换句话说,假如我就搞一个object出来,在上面直接安一个__get__,此object将不被视作descriptor(所以,定义了这三个方法的class/type object本身并不是descriptor。上面的英文描述并不完全准确。我太刨根问底儿了)。
光有了descriptor还不行,descriptor只有作为class/type object attribute才起作用。直接在某object上安一个descriptor,是达不到目的的。
这些都做好了之后,才是怎么引发那三个特殊方法。如果前面做的都对,那么从在instance object上对descriptor做reference, bind/rebind, 和unbind操作,会分别引发__get__, __set__, 和__delete__。此外,在class/type object上对descriptor做的reference操作也会引发__get__。
规则好复杂。。。龙与地下城的感觉又回来了,口胡。
2. __getattr__,__setattr__,以及__getattribute__
这三个也是必须定义为class/type object attribute,通过实例化得到这些方法的instance object,当其attribute被bind/rebind时,会被__setattr__拦截。当instance attribute被reference并且没找到时,触发__getattr__。
__getattribute__则拦截所有instance attribute reference,并且优先于__getattr__。后面我会说为什么。
3. 如果1和2同时出现。。。然后我们从instance object去bind/rebind或者reference那个descriptor的attribute:
bind/rebind时,__setattr__优先;reference时,既然有了descriptor,那肯定不会attribute找不到,因此__getattr__不会被调。但是__getattribute__仍优先于descriptor的__get__。
其实所谓谁优先什么的,嘿嘿,__getattribute__和__setattr__都是定义在built-in的object里面的。实际上descriptor包括__getattr__那些小把戏就是它们俩实现的。。。所以你把它们俩override以后,descriptor当然就不管用了。不信你在你的__getattribute__/__setattr__里面调object的那个,descriptor就又会冒出来了。。。看我下面最后一个测试代码,里面有。
也就是说只有__getattribute__和__setattr__是需要解释器特殊支持的,别的都可以用这俩实现。上面我说的那些优先级根本不用记。只要规则记住了就能用好这些。
强烈建议Guido把__setattr__改成__setattribute__。。。现在这样不是添乱麽!
测试代码:
test_descriptor.py
test_getsetattr.py
test_getsetattr_and_descriptor.py