属性(attribute)是什么
an attribute is a way to get from one object to another
属性就是另一个对象的引用,python中一切都是对象。函数、类、类的实例等等,都是对象。
python描述器(descriptor)是什么
在Descriptor HowTo Guide中这样定义descriptor
In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol.
也就是说,描述器其实是python对象中特殊的属性,这个属性的访问控制可以被重写。具体来说,当你获取(__get__),设置(__set__)和删除(__delete__)一个属性时,如果这个属性是描述器,那么就会调用被重写的函数,而不是默认的函数。
descriptor协议
Python通过descriptor协议来判断一个属性是否是描述器。如果一个属性定义了__get__, __set__或者__delete__方法中的任意一个,那么这个属性就是描述器,否则就是普通的属性。这三个方法也定义了这个属性被获取、设置或者删除时的行为。
descr.__get__(self, obj, type=None) --> valuedescr.__set__(self, obj, value) --> Nonedescr.__delete__(self, obj) --> None
调用过程
descriptor协议的具体工作过程是这样。
首先所有属性的调用都是通过__getattribute__()这个方法,descirptor的__get__()是在这个方法内被调用。
对象和类的调用过程有所不同,对象使用object.__getattribute__(),类则是使用type.getattribute__(),如果你对元类(metaclass)熟悉的话,应该能理解object是所有对象的type,type则是所有类的type。
对于类来说__getattribute__()大致是这样工作的
def __getattribute__(self, key):"Emulate type_getattro() in Objects/typeobject.c"v = type.__getattribute__(self, key)if hasattr(v, '__get__'):return v.__get__(None, self)return v
对于一般对象来说,则会返回v.get__(self,type(self))
描述器实例
现在我们可以定义自己的描述器了。
class Desc(object):def __init__(self):self.val = 0def __get__(self,obj,cls = None):print("call __get__")return self.valdef __set__(self,obj,val):print("call __set__")self.val = valclass MyClass(object):x = Desc()m = MyClass()print(m.x)m.x = 2print(m.x)
输出是
call __get__0call __set__call __get__2
这里x是m的一个属性,x有__get__和__set__方法,因此x是描述器。
两类描述器
描述器分为data descriptor和non-data descriptor。
定义了__get__和__set__的是data descriptor,只定义了__get__没有__set__的是non-data descriptor。
non-data descriptor没有__set__,因此在设置属性的时候会覆盖描述器,而不是调用描述器的__set__方法。
之前已经有data descriptor的例子,下面是non-data descriptor的例子。
class Desc(object):def __init__(self):self.val = 0def __get__(self,obj,cls = None):print("call __get__")return self.valclass MyClass(object):x = Desc()m = MyClass()print(m.x)m.x = 2print(m.x)
输出是
call __get__02
可以看到在m.x=2之后,m.x已经不是一个描述器,而是一个int对象了。
data descriptor可以完全控制(读和写)一个属性。在实例里面不能覆盖描述器,但仍然可以在类里面覆盖描述器。
non-data descriptor则只能读一个属性。对属性的任何修改都会覆盖描述器。
Python内置的描述器
描述器是Python中非常重要的一个特性,它用来方便地实现Python中的很多功能。
Python中内置的描述器包括 functions, properties, static methods, and class methods
函数作为描述器
函数是non-data descriptor
函数类大致是这样定义的
class Function(object):...def __get__(self, obj, objtype=None):"Simulate func_descr_get() in Objects/funcobject.c"return types.MethodType(self, obj, objtype)
所有函数都有__get__方法,因此是描述器。
>>> def f(self):pass>>> hasattr(f,'__get__')True>>> hasattr(f,'__set__')False>>> hasattr(f,'__delete__')False
接下来我们可以稍微考虑一下function和method的区别了
def myfunc(self):print("ok")class A(object):f = myfunca = A()print(myfunc)print(A.__dict__['f'])print(A.f)print(A.__dict__['f'].__get__(None,A))print(a.f)print(myfunc.__get__(a,type(a)))A.f(a)a.f()
<function myfunc at 0x000000000061CB70><function myfunc at 0x000000000061CB70><function myfunc at 0x000000000061CB70><function myfunc at 0x000000000061CB70><bound method A.myfunc of <__main__.A object at 0x000000000235C1D0>><bound method A.myfunc of <__main__.A object at 0x000000000235C1D0>>okok
稍微解释一下上面的输出。
首先myfunc和A.__dict__['f']是完全一样的,都是一个函数对象的引用。
A.f和A.__dict__['f'].__get__(None,A)代表的是一样的东西,后者是前者内部的实现过程。因为函数是一个描述器,所以获取A.f会调用f的__get__方法。正巧__get__方法返回的是f自身。也就是说,myfunc和A.f未必一样。
最后a.f和myfunc.__get__(a,type(a))是一样的。获取a.f会调用f的__get__方法,调用方式是__get__(a,type(a)),返回的结果是一个bound method,这是一个绑定了实例a的函数,a会作为默认参数传给函数的以一个参数。其中__get__方法对函数进行了绑定。
如果你想了解更多的细节,参考下面的资料:
Descriptor HowTo Guide
Python Attributes and Methods
Python Types and Objects


1142

被折叠的 条评论
为什么被折叠?



