描述器

描述器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')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值