Python描述器(Python Descriptors)

描述器 Descriptors

定义

  • 在Python中,类中实现了魔术方法__get__ __set__ __delete__三者中其一则称其为描述器。
  • 当一个类的属性为描述器时,访问这些属性会受到以上三者的控制。

魔术方法

只有当实现的类作为一个类的属性时,以下方法才起作用

实现__get__方法则称为非数据描述器

实现__get__和其他二者其一称为数据描述器

方法参数作用
__get__instance:属主实例
owner:属主类型
当实现类作为类属性时,通过类或者实例访问该属性时会调用该方法。访问到的值为该方法的返回值。
__set__instance
value:要修改的值,即等号右侧的值
当实现类作为类属性时,通过实例修改该属性时会调用该方法。实现该方法后,实例访问描述器属性时会优先访问类的。
__delete__instance当实现类作为类属性时,通过实例删除该属性时会调用该方法。实现该方法后,实例访问描述器属性时会优先访问类的。
class A:

    def __get__(self, instance, owner):
        print('get')

    def __set__(self, instance, value):
        print('set')

    def __delete__(self, instance):
        print('delelte')


class B:

    a = A()

b = B()

B.a
b.a
b.a = 1
del b.a

>>>get              
>>>get
>>>set
>>>delelte

通过B的类或实例在访问类的属性a时,由于a的类定义了__get__方法,会对其调用。

由于A定义了__set__方法,B的实例访问a属性时会优先访问类的属性,此时会调用__set__方法,而不会给实例新增属性。

作用

装饰器property

通过使用装饰器property,可以像访问类属性的方式来调用get、set、del方法,对属性访问加以控制。

class Property:
    
    def __init__(self, getter, setter=None, deleter=None):
        self.__get = getter
        self.__set = setter
        self.__del = deleter

    def setter(self, set):
        self.__set = set

        return self

    def deleter(self, dele):
        self.__del = dele

        return self

    def __get__(self, instance, owner):
        return self.__get(instance)

    def __set__(self, instance, value):
        self.__set(instance, value)

    def __delete__(self, instance):
        self.__del(instance)
        
class A():

    def __init__(self):
        self.__x = 1

    @Property
    def x(self):
        return self.__x

    @x.setter
    def x(self, value):
        self.__x = value

    @x.deleter
    def x(self):
        del self.__x

a = A()
a.x

>>>1
  • 在装饰get方法时,会调用Property类的初始化方法,将get方法加入到Property实例中,并返回Property实例对象。

  • 通过返回的Property实例调用类方法setter和deleter来装饰set方法和del方法,返回值必须是该Property实例对象。

  • 在A的属性中,x始终是一个Property对象。Property类是一个描述器,当使用实例来访问和修改属性时,就会调用Property的__get__ __set__ __delete__中的对应方法,在这些方法中会调用实例传进来的对应方法,从而达到像直接操作属性一般的效果。

类型检查

class Typed:

    def __init__(self, name, type):
        self.name = name
        self.type = type

    def __get__(self, instance, owner):

        return instance.__dict__[self.name] # 只能通过字典来访问 其他方式都会触发__get__方法

    def __set__(self, instance, value):

        print(value,self.type,self.name)
        if isinstance(value, self.type):
            instance.__dict__[self.name] = value
        else:
            raise TypeError('{} must {} type'.format(self.name, self.type))

    def __delete__(self, instance):

        del instance.__dict__[self.name]


class Test():

    name = Typed('name', str)
    age = Typed('age', int)
    def __init__(self, name, age):
        self.name = name
        self.age = age

利用描述器的特性,对类添加属性来控制实例的同名变量的修改。

直接在类中添加不够优雅

  • 使用inspect模块中来查看参数注解,自动来获取参数名以及类型数据
  • 使用装饰器来注入属性
import inspect


class Typed:

    def __init__(self, name, type):
        self.name = name
        self.type = type

    def __get__(self, instance, owner):

        return instance.__dict__[self.name]

    def __set__(self, instance, value):

        # print(value,self.type,self.name)
        if isinstance(value, self.type):
            instance.__dict__[self.name] = value
        else:
            raise TypeError('{} must {} type'.format(self.name, self.type))

    def __delete__(self, instance):

        del instance.__dict__[self.name]

# 使用函数装饰器,检查类初始化函数的参数名和类型注解来初始化Typed类动态写入到类中,并将其返回
def typeAssert(cls): 
    parameters = inspect.signature(cls).parameters

    for name, par in parameters.items():
        if par.annotation != par.empty:
            setattr(cls, name, Typed(name, par.annotation))

    return cls


# 使用类来做装饰器,将会返回一个对象。当把该实例当作类来实例化对象时,会在__call__中返回一个被装饰的类的实例
class TypeAssert:

    def __init__(self, cls):

        parameters = inspect.signature(cls).parameters
        for name, par in parameters.items():
            if par.annotation != par.empty:
                setattr(cls, name, Typed(name, par.annotation))
        self.__cls = cls

    def __call__(self, *args, **kwargs):

        return self.__cls(*args, **kwargs)


@TypeAssert
class Test():

    # name = Typed('name', str)
    # age = Typed('age', int)
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值