描述器的定义
描述器的其实就是一个类,实现了__get__,__set__和__delete__中的任何一个方法的类,这样的类就是描述器
描述器是一种代理机制,当一个类变量被定义为描述器的时候,对这个类变量的操作,将有此描述器代理
- 访问这个类变量会调用__get__方法
- 对这个类变量赋值会调用__set__方法
- 删除这个类变量的时候会调用__delete__方法
定义一个描述器
In [12]: class Int:
...: def __init__(self, val):
...: self.val = val
...: def __get__(self, instance, cls):
...: print(self.val)
...: def __set__(self, instance, cls):
...: self.val = 'test set'
...:
In [13]: class A:
...: val = Int('val') # 设置一个类变量,为上面类,也就是描述器
...: def __init__(self):
...: self.val = 3 # 在当前作用域中定义val为3
...:
In [14]: a = A() # 实例化 A
In [15]: a.val # 获取实例a的val,本来应该是3的,但是因为在实例的类中定义了一个类变量val为一个描述器,则类变量val的访问及设置的顺序就发生了改变
# 访问的时候就是在访问描述器的__get__方法,设置的时候就是调用了描述器的__set__方法
test set
描述器会提升优先级到高于dict之前,但是前提是描述器中存在set或者delete方法,如果描述器中不存在set或者delete方法的话,则低于dict的优先级
In [17]: a.__dict__
Out[17]: {}
In [18]: a.__dict__['val'] = 3 # 直接给实例添加了一个val的类变量,理论上来说,dict里面的类变量是高于类变量的
In [20]: a.__dict__
Out[20]: {'val': 3}
In [21]: a.val # 但是当我们访问的时候,还是得到了描述器的__get__的返回值
test set
In [22]: class Int: # 定义一个没有set方法的描述器
...: def __get__(self, instance, cls):
...: return 3
...:
In [25]: class A():
...: val = Int()
...: def __init__(self):
...: self.__dict__['val'] = 5 # 直接在dict中定义一个类变量
...:
In [26]: a = A()
In [27]: a.val # 访问实例a的类变量的时候,没有调用描述器的get方法
Out[27]: 5
__get__、__set__和__delete__的原始表达形态
- __get__(self, instance, cls) # instance:类实例, cls:类本身, 使用类访问的时候,instance为None
- __set__(self, instance, value) # instance:类实例, value:为值,只有实例才会调用__set__方法
- __delete__(self, instance) # instance:类实例
__get__
In [31]: class Desc:
...: def __get__(self, instance, cls): # 分别将instance和cls打印,看是什么东西
...: print(instance)
...: print(cls)
...: return 'x'
...:
In [32]: class A:
...: x = Desc()
...:
In [33]: A().x
<__main__.A object at 0x7f30cca44358> # 类的实例
<class '__main__.A'> # 类的本身
Out[33]: 'x'
In [34]: A.x # 通过类名来访问,无法获取到实例结果
None
<class '__main__.A'>
Out[34]: 'x'
__set__
In [35]: class Desc:
...: def __set__(self, instance, value):
...: print(instance)
...: print(value)
...:
In [36]: class A:
...: x = Desc()
...:
In [37]: A().x = 5 # 仅仅只能是实例才会调用
<__main__.A object at 0x7f30cca71cc0>
5
In [38]: A.x = 5
__delete__
In [39]: class Desc:
...: def __delete__(self, instance):
...: print(instance)
...:
In [40]: class A:
...: x = Desc()
...:
In [42]: del A().x # 删除类变量
<__main__.A object at 0x7f30ccaf6cf8>
描述器的应用场景
描述器是用于接管实例变量的操作
使用描述器来实现classmethod装饰器
from functools import partial # 固定方法所在位置
class Classmethod:
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, cls):
return partial(self.fn, cls)
使用描述器来实现staticmethod装饰器
from functools import partial
from functools import wraps
class Staticmethod:
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, cls):
return wraps(self.fn)(partial(self.fn, cls))