Python 描述符协议(descriptor protocol)
参考:
- Clean Code in Python Refactor your legacy codebase by Mariano Anaya (z-lib.org), Chapter 6
- Python document: Descriptor HowTo Guide
- Python implementing descriptors
描述符协议
Simple example
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class DescriptorClass(object):
value = 42
def __get__(self, instance, owner):
if instance is None:
return f"{self.__class__.__name__}.{owner.__name__}"
return f"value for {instance}"
class ClientClass(object):
descriptor = DescriptorClass()
def main():
client = ClientClass()
# output: DescriptorClass.ClientClass
print(ClientClass.descriptor)
# output: value for <__main__.ClientClass object at 0x...>
print(client.descriptor)
if __name__ == '__main__':
main()
__get__(self, instance, owner)
当我们希望获取描述符的值时, 该方法会被调用. 方法参数解释如下:
self : 表示 descriptor 对象本身
instance : 当 descriptor 被实例调用时, 表示该实例, 如果是被类调用, 则该参数为 None
owner : 表示 instance 参数的所属的类
因为 descriptor 是作为类的属性存在的, 所以是可以通过 ClientClass.descriptor 这种方式调用的. __get__ 的接口会看起来显得如此奇怪的原因就是为了保证不管是通过实例调用(obj.descriptor)还是通过类调用(Class.descriptor) , 该接口都能正常工作.
通常来说, 除非我们希望对 owner 进行一些操作, 否则当 instance 为 None 时, 一般返回描述符本身比较好.
__set__(self, instance, value)
当我们对描述符进行赋值操作时, 该方法会被调用. self 参数和 instance 参数同前, value 参数则表示被赋值的具体值.
当我们通过 client.descriptor = "value"
这样的调用方式对描述符进行赋值时, 如果描述符定义了 __set__ 方法,则会调用描述符的 __set__ 方法进行赋值, 此时 value 参数被设为 “value” . 如果描述符没有定义