描述器Descriptors
描述器的表现
用到3个魔术方法__get__() , __set__(),__delete__()
方法签名如下
object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete__(self,instance)
self 指当前实例,调用者
instance是owner的实例
owner是属性的所属的类
class A:
def __init__(self):
self.a1 = 'a1'
print(1,'abc')
def __get__(self, instance, owner):
print(3,'12313',self,instance,owner)
class B:
x = A()
def __init__(self):
self.x = 'xxxxxxx'
print(2,'B.init')
def add(self):
return 10
b = B()
print(b.x)
print(B.x)
print(b.__dict__)
print(B.__dict__)
看执行的顺序
因为定义了__get__方法,类A就是一个描述器,对类B或者类B的实例的x的属性读取,成为对类A的实例的访问,就会调用__get__方法
self,instance,owner这三个参数,是什么意思
b.x调用返回<__main__.A object at 0x0000026256DF47F0> <__main__.B object at 0x0000026256DF4780> <class '__main__.B'>
B.x调用返回<__main__.A object at 0x0000026256DF47F0> None <class '__main__.B'>
self对应都是A的实例
owner 对应都是B类
instance 说明
-None表示不是B类的实例,对应调用B.x
-<__main__.B object at 0x0000026256DF4780>表示是B的实例,对应调用B().x
只有类属性是类的实例才行
描述器定义
Python中,一个类实现了__get__,__set_,__delete_三个方法中的任何一个方法,就是描述器,实现这三个中的某些方法,就支持了描述器协议
- 仅实现了__get__,就是非数据描述符 non-data descriptor
- 实现了__get__ __set__就是数据描述符 data descritor
如果一个类的类属性设置为描述器实例,那么它被称为owner属主
当该类的该类属性被查找 设置 删除时,就会调用描述器相应的方法
属性的访问顺序
class A:
def __init__(self):
self.a1 = 'a1'
print(1,'abc')
def __get__(self, instance, owner):
print(3,'12313',self,instance,owner)
class B:
x = A()
def __init__(self):
self.x = 'xxxxxxx'
print(2,'B.init')
b = B()
print(b.x)
print(b.x.a1)
print(B.x.a1)
所有的b.x就会访问描述器的__get__(方法),代码中返回的self就是描述器实例,它的实例字典中就保存着a1和data属性,可以打印b.x.__dict__就可以看到这些属性
属性查找顺序
实例的__dict__优先于 非数据描述器
数据描述器优先于实例的__dict__
__delete__方法有同样的效果,有了这个方法,也是数据描述器
Python中的描述器
描述器在Python中应用非常广泛
Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器.因此,实例可以重新定义和覆盖方法,这允许单个实例获取与同一类的其他实例不同的行为
property()函数实现为一个数据描述器.因此,实例不能覆盖属性的行为
新增方法
python3.6新增描述器方法__set_name__,它在属主类构建的时候就会调用
这个方法,就是可以知道属主类和属主类的类的属性名
对实例的数据进行效验
import inspect
class TypeCheck:
def __init__(self,name,typ):
self.name = name
self.type = typ
def __get__(self,instance,owner):
print('get---------')
if insatnce:
return instance.__dict__[self.name]
return self
def __set__(self,instance,value):
print('set~~~~~~~~~~')
if not isinstance(value,self.type):
raise TypeError(value)
instance.__dict__[self.name] = value
def typeinject(cls):
sig = inspect.signature(cls)
params = sig.parameters
for name,param in params.items():
print(name,param)
if param.annotation != param.empty:
setattr(cls,name,TypeCheck(name,param.annotation))
return cls
@typeinject
class Person:
# name = TypeCheck('name',str) #硬编码
# age = TypeCheck('age',int) #不优雅
def __init__(self,name:str,age:int):
self.name = name
self.age = age
print(Person.__dict__)
p1 = Person('jerry','18')
类装饰器
import inspect
class TypeCheck:
def __init__(self,name,typ):
self.name = name
self.type = typ
def __get__(self,instance,owner):
print('get---------')
if insatnce:
return instance.__dict__[self.name]
return self
def __set__(self,instance,value):
print('set~~~~~~~~~~')
if not isinstance(value,self.type):
raise TypeError(value)
instance.__dict__[self.name] = value
class TypeInject:
def __init__(self,cls):
self.cls = cls
sig = inspect.signature(cls)
params = sig.parameters
for name,param in params.items():
print(name,param)
if param.annotation != param.empty:
setattr(cls,name,TypeCheck(name,param.annotation))
def __call__(self,*args,**kwargs):
return self.cls(*args,**kwargs)
@TypeInject
class Person:
def __init__(self,name:str,age:int):
self.name = name
self.age = age
print(Person.__dict__)
p1 = Person('jerry','18')