描述器Descriptors
描述器的表现
描述器需要用到三个魔术方法:get(),set(),delete()。
方法签名如下:
- object.get(self,instance,owner)
- object.set(self,instance,owner)
- object.delete(self,instance)
self指代当前实例,调用者
instance是owner的实例
owner是属性的所属的类
class A:
def __init__(self):
self.a1='a1'
print('A.init')
def __get__(self, instance, owner):
print(self,instance,owner)
class B:
x=A()
def __init__(self):
print('B.init')
print(B.x)
b=B()
print(b.x)
定义了__get__()方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问,就会调用__get__方法
使用B的实例调取A的属性失败
观察self,instance,owner三个参数的含义。
get(self,instance,owner)方法的签名,会传入3个参数
B.x调用返回,<main.A object at 0x0000022363040E10> None <class ‘main.B’>
b.x调用返回,<main.A object at 0x0000022363040E10> <main.B object at 0x0000022363040E80> <class ‘main.B’>
self对用都是A的实例
owner对应都是B类
instance说明:
- None表示不是B类的实例,对应调用B.x
- <main.B object at 0x0000022363040E80> 表示是B的实例,对应调用B().x
使用返回值解决,返回self,就是A的实例,该实例中有a1属性。
类B中的实例属性:
class A:
def __init__(self):
self.a1='a1'
print('A.init')
def __get__(self, instance, owner):
print(self,instance,owner)
return self
class B:
x=A()
def __init__(self):
print('B.init')
self.b=A()
print(B.x)
b=B()
print(b.x)
print(b.x.a1)
print(b.b)
从运行结果来看,只有类属性是类的实例才行。
描述器定义
Python中,一个类实现了__get__、set、__delete__三个方法中的任何一个方法,就是描述器,实现这三个中的某些方法,就支持了描述器协议。
- 仅实现了__get__,就是非数据描述符non-data descriptor
- 实现了__get__、__set__就是数据描述符data descriptor
如果一个类的类属性设置为描述器实例,那么它被称为owner属主
当该类的该类属性被查找、设置、删除时,就会调用描述器相应的方法。
属性的访问顺序
class A:
def __init__(self):
self.a1='a1'
print('A.init')
def __get__(self, instance, owner):
print(self,instance,owner)
return self
class B:
x=A()
def __init__(self):
print('B.init')
self.x='b.x'
print(B.x)
print(B.x.a1)
b=B()
print(b.x)
print(b.x.a1)
print(b.x)
类A只实现了__get__()方法,b.x访问到了实例的属性,而不是描述器
class A:
def __init__(self):
self.a1='a1'
print('A.init')
def __get__(self, instance, owner):
print(self,instance,owner,'---get---')
return self
def __set__(self, instance, value,):
print(self,instance,value,'---set---')
class B:
x=A()
def __init__(self):
print('B.init')
self.x='b.x.1'
print(B.x)
print(B.x.a1)
b=B()
print(b.x)
print(b.x.a1)
print(b.x)
所有b.x就会访问__get__()方法,代码中返回self就是描述器实例
属性查找顺序:
实例的__dict__优先于非数据描述器
数据描述器优先于实例的__dict__
delete__方法有同样的效果,有了这个方法们也是数据描述器
Python中的描述器
描述器在Python中应用非常广泛
Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器,因此,实例可以重新定义和覆盖方法,这允许单个实例获取与同一类的其他实例不同的行为。
property()函数实现为一个数据描述器,因此,实例不能覆盖属性的行为。
新增方法
3.6新增描述器方法__set_name,它在属主类构建的时候就会调用
class A:
def __init__(self):
self.a1='a1'
print('A.init')
def __get__(self, instance, owner):
print(self,instance,owner,'get')
return self
def __set_name__(self,owner,name):
print(self,owner,name,'name')
class B:
x=A()
提供这个方法,就是可以知道属主类和属主类的类属性名