先看一个例子,@property. 被 @property 修饰的成员函数, 将变为一个描述符. 这是最简单的创建描述符的方式.
- class Foo:
- @property
- def attr(self):
- print('getting attr')
- return 'attr value'
- def bar(self): pass
- foo = Foo()
上面这个例子中, attr 是类 Foo 的一个成员函数, 可通过语句 foo.attr() 被调用. 但当它被 @property 修饰后, 这个成员函数将不再是一个函数, 而变为一个描述符. bar 是一个未被修饰的成员函数. type(Foo.attr) 与 type(Foo.bar) 的结果分别为:
- <type 'property'>
- <type 'instancemethod'>
attr 的类型为 property (注: 一个 property 类型的对象总是一个描述符), bar 的类型为 instancemethod , 也即一个常规的成员函数.
此时 attr 将无法再被调用, 当尝试调用它时, 语句 foo.attr() 将抛出错误:
TypeError: 'str' object is not callable
让我们来理解这个错误.
首先来看 foo.attr 的值:
attr value
其类型 type(foo.attr) :
str
foo.attr 的类型为 str , 因此便有了以上的错误, 一个 str 对象无法被调用. 其值为'attr value', 正好是原始 attr 函数的返回值. 因此语句 foo.attr 实际上触发了原始 attr 函数的调用, 并且将函数的返回值作为其值. 实际上语句 print(foo.attr) 的输出为:
- getting attr
- attr value
进一步验证了执行语句 foo.attr 时, 原始的 attr 函数被调用.
发生了什么? 当执行一个访问对象属性的语句 foo.attr 时, 结果一个函数调用被触发! 这便是描述符的作用: 将属性访问转变为函数调用, 并由这个函数来控制这个属性的值 (也即函数的返回值), 以及在返回值前做定制化的操作. 此时可以给描述符一个简要定义:
描述符是类的一个属性, 控制类实例对象访问这个属性时如何返回值及做哪些额外操作
这留给程序员的空间是巨大的..
描述符协议
任何实现了描述符协议的类都可以作为描述符类. 描述符协议为一组成员函数定义, 包括:
函数 | 作用 | 返回值 | 是否必须 |
---|---|---|---|
__get__(self, obj, type) | 获取属性值 | 属性的值 | 是 |
__set__(self, obj, value) | 设置属性的值 | None | 否 |
__delete__(self, obj) | 删除属性 | None | 否 |
如果一个类实现了以上成员函数, 则它便是一个描述符类, 其实例对象便是一个描述符
下面是一个自定义的描述符的实现.
- class MyDescriptor:
- def __init__(self):
- self.data = None
- def __get__(self, obj, type):
- print('get called')
- return self.data
- def __set__(self, obj, value):
- print('set called')
- self.data = value
- def __delete__(self, obj):
- print('delete called')
- del self.data
- class Foo:
- attr = MyDescriptor()
- foo = Foo()
示例中 MyDescriptor 实现了描述符协议 (也即实现了
__get__, __set__, __delete__
函数), 因此其为一个描述符类. Foo 的 attr 属性为 MyDescriptor 类的实例对象, 因此它是一个描述符.
print(foo.attr) 的输出为:
get called None
可见当访问 foo 的 attr 属性时, MyDescriptor 的 __get__ 函数被调用.
foo.attr = 'new value' 的输出为:
set called
可见当为 attr 设置一个新值时, MyDescriptor 的 __set__ 函数被调用.
再运行 print(foo.attr) , 输出为:
- get called
- new value
可见新值已被设置.
del foo.attr 的输出为:
delete called
可见当为删除属性 attr 时, MyDescriptor 的 __delete__ 函数被调用.
再执行 print(foo.attr) , AttributeError 被抛出:
- get called
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "1.py", line 6, in __get__
- return self.data
- AttributeError: 'MyDescriptor' object has no attribute 'data'
可见属性 attr 已被删除.
参数意义
__get__(self, obj, type)
函数各个参数的意义为:
参数 | 意义 | 例子中的对应 |
---|---|---|
self | 描述符对象本身 | Foo.attr |
obj | 使用描述符的对象实例 | foo |
type | obj 的类型 | Foo |
__set__(self, obj, value)
函数的 self 和 obj 参数的意义同 __get__ ,value 的意义为:
参数 | 意义 | 例子中的对应 |
---|---|---|
value | 属性的新值 | 'new value' |
__delete__(self, obj)
函数的 self 和 obj 参数的意义同 __get__ .
(全文完)
来源: https://www.cnblogs.com/astropeak/p/9032271.html