编写高质量代码:改善Python程序的91个建议这书里的建议59说的是“理解描述符”,描述符是descriptor的翻译。什么是descriptor,descriptor能用来做什么,其实Python的官方文档Descriptor HowTo Guide说得更简单易懂。在阅读完它们之后,自己也来尝试着总结记录一下。
Descriptor是一种约定
Python的descriptor的约定如下,
- 定义了__get__、__set__、__delete__三个中任意一个的object即为descriptor
- 定义了__get__、__set__的为data descriptor,否则为non-data descriptor
三个函数的函数签名如下,
__get__(self, obj, type=None)
__set__(self, obj, value)
__delete__(self, obj)
Descriptor如何发生作用
Python约定了descriptor里定义的几个函数如何被触发,
- descriptor是在__getattribute__方法中被调用的
调用object.xxx与type.xxx触发的行为不一样,
- object.xxx会触发object.__getattribute__,进而转换成object.__dict__['xxx'].__get__(object, type(object))
- type.xxx会触发type.__dict__['xxx'].__get__(None, type)
看到descriptor的两种不同触发行为是不是就能够联想到Python中的staticmethod、classmethod。Descriptor HowTo Guide里给出了staticmethod、classmethod对应的Python实现,这里直接引用下,
class StaticMethod(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
return self.func
class ClassMethod(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def new_func(*args):
return self.func(klass, *args)
return new_func
除了object,class默认提供的__getattribute__之外,super()返回的对象也包含了__getattribute__方法,于是也会走descriptor的触发逻辑。
此外,Python的function、method的区别、bound method、unbound method也是以类似机制实现的。
Descriptor的注意事项
- 只有new-style class(继承自object或type的类)才会触发descriptor行为
- 重载了类的__getattribute__方法后会导致descriptor行为不会自动触发
总结
Descriptor的实质很简单,就是约定了某个对象上如果定义了相应的函数则其为descriptor,Python在调用__getattibute__时会对descriptor进行特定的处理。对于使用者来说,我们需要知道的就是这些特定处理的规则是怎样的,以及从已有的一些用例中寻找其合适的使用方式。
另外,如果面试需要考察Python相关知识的话,考察对descriptor的理解会是个不错的问题。