描述器和装饰器是两个不同的东西,描述器是类中封装了__set__(), __get__(), __delete__()等魔术方法。
python中的属性、实例方法、静态方法、类方法、super都是基于描述器实现的
python中默认的对属性的访问控制是从对象字典__dict__中进行封装的,以obj.x举例,它的默认查找顺序是
先查找实例的__dict__即obj.__dict__[‘x’]
若查找不到,再查找type(obj).__dict__[‘x’]
若查找不到,最后查找type(obj)的父类(不包括__metaclass__)
但是当定义了描述器时,python就会调用描述器的方法来重写默认的控制行为
描述器的分类
资料描述器:同时定义了__set__(),和__get__()方法
非资料描述器:仅定义了__get__()方法
它们的区别在于:主要区别在于实例字典的优先级上。如果访问的属性在字典实例中且是资料描述器那么以资料描述器的为主,否则若是非资料描述器则以实例字典的为主。
与__setattr__(),__getattr__(),__getattribute__()的关系
__setattr__()的优先级高于描述器的__set__()的方法
如果属性已经定义了那么不会再执行__getattr__()了,而是直接通过访问实例字典返回结果,但如果存在描述器则走描述器的__get__()方法,__getattr__()只在访问未定义的属性时被触发
__getattribute__():在调用类方法,字典__dict__属性时会通过访问此魔术方法实现,如果同一个类中也封装了__setattr__()且操作了self.__dict__那么将会陷入无限循环
描述器的调用也是因为__getattribute__(),当重写了__getattribute__()时会阻止描述器的调用
实例
描述器的基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class (object):
'''Descriptor for a meter.'''
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Foot(object):
'''Descriptor for a foot.'''
def __get__(self, instance, owner):
return instance.meter * 3.2808
def __set__(self, instance, value):
instance.meter = float(value) / 3.2808
class Distance(object):
meter = Meter()
foot = Foot()
d = Distance()
print(d.meter, d.foot)
d.meter = 1
print(d.meter, d.foot) # 1.0 3.2808
d.meter = 2
print(d.meter, d.foot) # 2.0 6.5616
存在__setattr__()时的资料描述器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class (object):
'''Descriptor for a meter.'''
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Foot(object):
'''Descriptor for a foot.'''
def __get__(self, instance, owner):
return instance.meter * 3.2808
def __set__(self, instance, value):
instance.meter = float(value) / 3.2808
class Distance(object):
meter = Meter()
foot = Foot()
def __setattr__(self, name, value):
print("__setattr__")
self.__dict__[name] = value
d = Distance()
print(d.meter, d.foot)
d.meter = 1
print(d.meter, d.foot)
d.meter = 2
print(d.meter, d.foot)
输出:1
2
3
4
50.0 0.0
__setattr__
0.0 0.0
__setattr__
0.0 0.0
由于资料描述器的__get__()优先级高于实例字典,故会一致访问资料描述器的值,__setattr__()的优先级高于描述器的__set__()所以更新的是实例字典的值
存在__setattr__()时的非资料描述器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class (object):
'''Descriptor for a meter.'''
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
class Foot(object):
'''Descriptor for a foot.'''
def __get__(self, instance, owner):
return instance.meter * 3.2808
class Distance(object):
meter = Meter()
foot = Foot()
def __setattr__(self, name, value):
print("__setattr__")
self.__dict__[name] = value
d = Distance()
print(d.meter, d.foot)
d.meter = 1
print(d.meter, d.foot)
d.meter = 2
print(d.meter, d.foot)
输出1
2
3
4
50.0 0.0
__setattr__
1 3.2808
__setattr__
2 6.5616
实例字典的优先级高于非资料描述器
Refrence
[1] https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html#id11