Python描述器

属性(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__方法中的任意一个,那么这个属性就是描述器,否则就是普通的属性。这三个方法也定义了这个属性被获取、设置或者删除时的行为。

 
 
  1. descr.__get__(self, obj, type=None) --> value
  2. descr.__set__(self, obj, value) --> None
  3. descr.__delete__(self, obj) --> None

调用过程

descriptor协议的具体工作过程是这样。
首先所有属性的调用都是通过__getattribute__()这个方法,descirptor的__get__()是在这个方法内被调用。
对象和类的调用过程有所不同,对象使用object.__getattribute__(),类则是使用type.getattribute__(),如果你对元类(metaclass)熟悉的话,应该能理解object是所有对象的type,type则是所有类的type。
对于类来说__getattribute__()大致是这样工作的

 
 
  1. def __getattribute__(self, key):
  2. "Emulate type_getattro() in Objects/typeobject.c"
  3. v = type.__getattribute__(self, key)
  4. if hasattr(v, '__get__'):
  5. return v.__get__(None, self)
  6. return v

对于一般对象来说,则会返回v.get__(self,type(self))

描述器实例

现在我们可以定义自己的描述器了。

 
 
  1. class Desc(object):
  2. def __init__(self):
  3. self.val = 0
  4. def __get__(self,obj,cls = None):
  5. print("call __get__")
  6. return self.val
  7. def __set__(self,obj,val):
  8. print("call __set__")
  9. self.val = val
  10. class MyClass(object):
  11. x = Desc()
  12. m = MyClass()
  13. print(m.x)
  14. m.x = 2
  15. print(m.x)

输出是

 
 
  1. call __get__
  2. 0
  3. call __set__
  4. call __get__
  5. 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的例子。

 
 
  1. class Desc(object):
  2. def __init__(self):
  3. self.val = 0
  4. def __get__(self,obj,cls = None):
  5. print("call __get__")
  6. return self.val
  7. class MyClass(object):
  8. x = Desc()
  9. m = MyClass()
  10. print(m.x)
  11. m.x = 2
  12. print(m.x)

输出是

 
 
  1. call __get__
  2. 0
  3. 2

可以看到在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
函数类大致是这样定义的

 
 
  1. class Function(object):
  2. ...
  3. def __get__(self, obj, objtype=None):
  4. "Simulate func_descr_get() in Objects/funcobject.c"
  5. return types.MethodType(self, obj, objtype)

所有函数都有__get__方法,因此是描述器。

 
 
  1. >>> def f(self):
  2. pass
  3. >>> hasattr(f,'__get__')
  4. True
  5. >>> hasattr(f,'__set__')
  6. False
  7. >>> hasattr(f,'__delete__')
  8. False

接下来我们可以稍微考虑一下function和method的区别了

 
 
  1. def myfunc(self):
  2. print("ok")
  3. class A(object):
  4. f = myfunc
  5. a = A()
  6. print(myfunc)
  7. print(A.__dict__['f'])
  8. print(A.f)
  9. print(A.__dict__['f'].__get__(None,A))
  10. print(a.f)
  11. print(myfunc.__get__(a,type(a)))
  12. A.f(a)
  13. a.f()
 
 
  1. <function myfunc at 0x000000000061CB70>
  2. <function myfunc at 0x000000000061CB70>
  3. <function myfunc at 0x000000000061CB70>
  4. <function myfunc at 0x000000000061CB70>
  5. <bound method A.myfunc of <__main__.A object at 0x000000000235C1D0>>
  6. <bound method A.myfunc of <__main__.A object at 0x000000000235C1D0>>
  7. ok
  8. ok

稍微解释一下上面的输出。
首先myfuncA.__dict__['f']是完全一样的,都是一个函数对象的引用。
A.fA.__dict__['f'].__get__(None,A)代表的是一样的东西,后者是前者内部的实现过程。因为函数是一个描述器,所以获取A.f会调用f的__get__方法。正巧__get__方法返回的是f自身。也就是说,myfunc和A.f未必一样。
最后a.fmyfunc.__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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值