Python描述符 (descriptor) 详解
2021-06-23 05:03
标签:它的 理解 a20 python 直接 UNC attribute 为什么 作用 1、什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。 以上为官方定义,纯粹为了装逼使用,一般人看这些定义都有一种问候祖先的冲动! 没关系,看完本文,你就会理解什么叫描述符了! 2、讲解描述符前,先看一下属性:__dict__ (每个对象均具备该属性) 作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value} 对象属性的访问顺序: ①.实例属性 ②.类属性 ③.父类属性 ④.__getattr__()方法 以上顺序,切记切记! 从以上代码可以看出,实例t的属性并不包含cls_val,cls_val是属于类Test的。 3、魔法方法:__get__(), __set__(), __delete__() 方法的原型为: ① __get__(self, instance, owner) ② __set__(self, instance, value) ③ __del__(self, instance) 那么以上的 self, instance owner到底指社么呢?莫急莫急,听我慢慢道来! 首先我们先看一段代码: 可以看到,实例化类TestDesc后,调用对象t访问其属性x,会自动调用类Desc的 __get__方法,由输出信息可以看出: ① self: Desc的实例对象,其实就是TestDesc的属性x ② instance: TestDesc的实例对象,其实就是t ③ owner: 即谁拥有这些东西,当然是 TestDesc这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的 到此,我可以揭开小小的谜底了,其实,Desc类就是是一个描述符(描述符是一个类哦),为啥呢?因为类Desc定义了方法 __get__, __set__. 所以,某个类,只要是内部定义了方法 __get__, __set__, __delete__ 中的一个或多个,就可以称为描述符(^_^,简单吧) 说到这里,我们的任务还远远没有完成,还存在很多很多的疑点? 问题1. 为什么访问 t.x的时候,会直接去调用描述符的 __get__() 方法呢? 答:t为实例,访问t.x时,根据常规顺序, 首先:访问Owner的__getattribute__()方法(其实就是 TestDesc.__getattribute__()),访问实例属性,发现没有,然后去访问父类TestDesc,找到了! 其次:判断属性 x 为一个描述符,此时,它就会做一些变动了,将 TestDesc.x 转化为 TestDesc.__dict__[‘x‘].__get__(None, TestDesc) 来访问 然后:进入类Desc的 __get__()方法,进行相应的操作 问题2. 从上面 代码1 我们看到了,描述符的对象 x 其实是类 TestDesc 的类属性,那么可不可以把它变成实例属性呢? 答:我说了你不算,你说了也不算,解释器说了算,看看解释器怎么说的。 咦,为啥没打印 t.y 的信息呢? 因为没有访问 __get__() 方法啊,哈哈,那么为啥没有访问 __get__() 方法呢?(问题真多) 因为调用 t.y 时刻,首先会去调用TestDesc(即Owner)的 __getattribute__() 方法,该方法将 t.y 转化为TestDesc.__dict__[‘y‘].__get__(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,所以,只能忽略了。 问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整? 答:还是让解释器来解释一下吧。 不对啊,按照惯例,t._x 会去调用 __getattribute__() 方法,然后找到了 实例t 的 _x 属性就结束了,为啥还去调用了描述符的 __get__() 方法呢? 这就牵扯到了一个查找顺序问题:当Python解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。 不信?来看一下 字典 : 怎么样,没骗你吧?我这人老好了,从来不骗人! 我们再将 代码3 改进一下, 删除 __set__() 方法试试看会发生什么情况? 我屮艸芔茻,咋回事啊?怎么木有去 调用 __get__() 方法? 其实,还是 属性 查找优先级惹的祸,只是定义一个 __get__() 方法,为非数据描述符,优先级低于实力属性的!! 问题4. 什么是数据描述符,什么是非数据描述符? 答:一个类,如果只定义了 __get__() 方法,而没有定义 __set__(), __delete__() 方法,则认为是非数据描述符; 反之,则成为数据描述符 问题5. 天天提属性查询优先级,就不能总结一下吗? 答:好的好的,客官稍等! ① __getattribute__(), 无条件调用 ② 数据描述符:由 ① 触发调用 (若人为的重载了该 __getattribute__() 方法,可能会调职无法调用描述符) ③ 实例对象的字典(若与描述符对象同名,会被覆盖哦) ④ 类的字典 ⑤ 非数据描述符 ⑥ 父类的字典 ⑦ __getattr__() 方法 Python描述符 (descriptor) 详解 标签:它的 理解 a20 python 直接 UNC attribute 为什么 作用 原文地址:https://www.cnblogs.com/leesen934/p/9674699.html 1 class Test(object):
2 cls_val = 1
3 def __init__(self):
4 self.ins_val = 10
5
6
7 >>> t=Test()
8 >>> Test.__dict__
9 mappingproxy({‘__module__‘: ‘__main__‘, ‘cls_val‘: 1, ‘__init__‘:
1 #代码 1
2
3 class Desc(object):
4
5 def __get__(self, instance, owner):
6 print("__get__...")
7 print("self : \t\t", self)
8 print("instance : \t", instance)
9 print("owner : \t", owner)
10 print(‘=‘*40, "\n")
11
12 def __set__(self, instance, value):
13 print(‘__set__...‘)
14 print("self : \t\t", self)
15 print("instance : \t", instance)
16 print("value : \t", value)
17 print(‘=‘*40, "\n")
18
19
20 class TestDesc(object):
21 x = Desc()
22
23 #以下为测试代码
24 t = TestDesc()
25 t.x
26
27 #以下为输出信息:
28
29 __get__...
30 self : <__main__.desc object at>
31 instance : <__main__.testdesc object at>
32 owner :
1 #代码 2
2
3 class Desc(object):
4 def __init__(self, name):
5 self.name = name
6
7 def __get__(self, instance, owner):
8 print("__get__...")
9 print(‘name = ‘,self.name)
10 print(‘=‘*40, "\n")
11
12 class TestDesc(object):
13 x = Desc(‘x‘)
14 def __init__(self):
15 self.y = Desc(‘y‘)
16
17 #以下为测试代码
18 t = TestDesc()
19 t.x
20 t.y
21
22 #以下为输出结果:
23 __get__...
24 name = x
25 ========================================
1 #代码 3
2
3 class Desc(object):
4 def __init__(self, name):
5 self.name = name
6 print("__init__(): name = ",self.name)
7
8 def __get__(self, instance, owner):
9 print("__get__() ...")
10 return self.name
11
12 def __set__(self, instance, value):
13 self.value = value
14
15 class TestDesc(object):
16 _x = Desc(‘x‘)
17 def __init__(self, x):
18 self._x = x
19
20
21 #以下为测试代码
22 t = TestDesc(10)
23 t._x
24
25 #输入结果
26 __init__(): name = x
27 __get__() ...
1 >>> t.__dict__
2 {}
3
4 >>> TestDesc.__dict__
5 mappingproxy({‘__module__‘: ‘__main__‘, ‘_x‘: <__main__.desc object at>, ‘__init__‘:
1 #代码 4
2
3 class Desc(object):
4 def __init__(self, name):
5 self.name = name
6 print("__init__(): name = ",self.name)
7
8 def __get__(self, instance, owner):
9 print("__get__() ...")
10 return self.name
11
12 class TestDesc(object):
13 _x = Desc(‘x‘)
14 def __init__(self, x):
15 self._x = x
16
17
18 #以下为测试代码
19 t = TestDesc(10)
20 t._x
21
22 #以下为输出:
23 __init__(): name = x