Python描述符 (descriptor) 详解
1、什么是描述符?
python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 get(), set(), 和delete()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。
以上为官方定义,纯粹为了装逼使用,一般人看这些定义都有一种问候祖先的冲动!
没关系,看完本文,你就会理解什么叫描述符了!
2、讲解描述符前,先看一下属性:dict (每个对象均具备该属性)
作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}
对象属性的访问顺序:
①.实例属性
②.类属性
③.父类属性
④.getattr()方法
以上顺序,切记切记!
按 Ctrl+C 复制代码
class Test(object):
cls_val = 1
def init(self):
self.ins_val = 10
t=Test()
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 1, ‘init‘:
更改实例t的属性cls_val,只是新增了该属性,并不影响类Test的属性cls_val
t.cls_val = 20
t.dict
{‘ins_val’: 10, ‘cls_val’: 20}
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 1, ‘init‘:
更改了类Test的属性cls_val的值,由于事先增加了实例t的cls_val属性,因此不会改变实例的cls_val值(井水不犯河水)
Test.cls_val = 30
t.dict
{‘ins_val’: 10, ‘cls_val’: 20}
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 30, ‘init‘:
代码 1
class Desc(object):
def __get__(self, instance, owner):
print("__get__...")
print("self : \t\t", self)
print("instance : \t", instance)
print("owner : \t", owner)
print('='*40, "\n")
def __set__(self, instance, value):
print('__set__...')
print("self : \t\t", self)
print("instance : \t", instance)
print("value : \t", value)
print('='*40, "\n")
class TestDesc(object):
x = Desc()
以下为测试代码
t = TestDesc()
t.x
以下为输出信息:
get…
self : <main.Desc object at 0x0000000002B0B828>
instance : <main.TestDesc object at 0x0000000002B0BA20>
owner :
代码 2
class Desc(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print("__get__...")
print('name = ',self.name)
print('='*40, "\n")
class TestDesc(object):
x = Desc('x')
def __init__(self):
self.y = Desc('y')
以下为测试代码
t = TestDesc()
t.x
t.y
以下为输出结果:
get…
name = x
咦,为啥没打印 t.y 的信息呢?
因为没有访问 get() 方法啊,哈哈,那么为啥没有访问 get() 方法呢?(问题真多)
因为调用 t.y 时刻,首先会去调用TestDesc(即Owner)的 getattribute() 方法,该方法将 t.y 转化为TestDesc.dict[‘y’].get(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,所以,只能忽略了。
问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整?
答:还是让解释器来解释一下吧。
代码 3
class Desc(object):
def __init__(self, name):
self.name = name
print("__init__(): name = ",self.name)
def __get__(self, instance, owner):
print("__get__() ...")
return self.name
def __set__(self, instance, value):
self.value = value
class TestDesc(object):
_x = Desc('x')
def __init__(self, x):
self._x = x
以下为测试代码
t = TestDesc(10)
t._x
输入结果
init(): name = x
get() …
不对啊,按照惯例,t._x 会去调用 getattribute() 方法,然后找到了 实例t 的 _x 属性就结束了,为啥还去调用了描述符的 get() 方法呢?
这就牵扯到了一个查找顺序问题:当Python解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。
不信?来看一下 字典 :
复制代码
1 >>> t.dict
2 {}
3
4 >>> TestDesc.dict
5 mappingproxy({‘module‘: ‘main‘, ‘_x’: <main.Desc object at 0x0000000002B0BA20>, ‘init‘: