python高级(描述符相关的魔术方法)

描述符使用

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

描述符是可以实现大部分Python类特性中的底层魔法,包括@classmethod@staticmethod@property甚至是__slot__属性

描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件

 

1 描述符是什么:

描述符本质就是一个新式类,在这个新式类中,至少实现了以下__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

 

__get__():调用一个属性时,触发

触发时机:在获取指定描述符操作的成员属性的值的时候触发 参数:1描述符对象本身,2描述符描述的属性所在的对象,描述符描述的对象的类 返回值:必须有,不然无法获取相应属性值 注意:仅在描述符中使用

__set__():为一个属性赋值时,触发

触发时机:在设置或者添加指定描述符操作的成员属性的时候触发 参数:1描述符对象本身,2描述符描述的属性所在的对象,3要设置的值 返回值:无 注意:仅在描述符中使用

__delete__():采用del删除属性时,触发

触发时机:在删除指定描述符操作的成员属性的时候触发 参数:1描述符对象本身,2描述符描述的属性所在的对象 返回值:无 注意:仅在描述符中使用。

 

2 描述符是干什么的:

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

 

3 描述符分两种
一 数据描述符:至少实现了__get__()和__set__()

1 class Foo:
2     def __set__(self, instance, value):
3         print('set')
4     def __get__(self, instance, owner):
5         print('get')

二 非数据描述符:没有实现__set__()

1 class Foo:
2     def __get__(self, instance, owner):
3         print('get')

 

小结:

一 描述符本身应该定义成新式类,被代理的类也应该是新式类,代理类必须实现三个描述符当中其中一个方法。

二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中

三 要严格遵循该优先级,优先级由高到底分别是

1.类属性

2.数据描述符

3.实例属性

4.非数据描述符

5.找不到的属性触发__getattr__()

 

案例:

class MSF():  #MSF自己定义的描述符

    def __init__(self):

         self.num = 20

         #obj就是下面把这个被代理类实例化出的对象age

         #cls就是自己MSF这个类。

    def __get__(self, obj, cls):

         return self.num

    def __set__(self, obj, value):

         if value > 100 or value < 1:

              print("设置的值不在范围之内")

         else:

              self.num = value

    def __delete__(self, obj):

         if obj.allow_delete:

              del self.num

         else:

              print('不允许删除')

 

class Student():  #代理的描述类就是一个新式类

     age = MSF()  #实例化对象,代理的类必须实例化上面被代理的类

     allow_delete = True  #根据情况设置是否允许删除

 

s = Student()

s.age = 90  #设置值的时候会触发__set__

print(s.age)  #获取值的时候会触发__get__

del s.age  #删除值的时候会触发__del__

print(s.age)

 

描述符优先级

class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')
    def __set__(self, instance, value):
        print('===>set方法',instance,value)
        instance.__dict__['x']=value  #b1.__dict__
    def __delete__(self, instance):
        print('===>delete方法')


class Bar:
    x=Foo() #在何地?

print(Bar.x)  #类属性字典中找不到,触发描述符__get__方法,因为还没赋值,返回None

Bar.x=1    #设置类属性,类属性优先级 > 描述符优先级,不会触发描述符__set__方法
print(Bar.__dict__)  #查看底层字典,键值对已写进去覆盖
print(Bar.x)  #不会触发描述符__get__方法,因为优先找到了底层字典中的类属性值,类属性优先级 > 数据描述符,直接返回结果
b1=Bar()  
b1.x   #触发__get__,首先到实例化b1的字典中没有找到,再去类中的字典中找触发数据描述符,数据描述符优先级 > 实例属性
b1.x=1 #触发__set__,首先到实例化b1的字典中没有找到,再去类中的字典中设置触发数据描述符
del b1.x #触发__delete__,首先到实例化b1的字典中没有找到,再去类中的字典中删除触发数据描述符


Bar.x=111111111111    #设置类属性
b1.x   #不会触发描述符__get__方法, 类属性优先级 >  实例属性

del Bar.x    #删除类属性
b1.x   #因为类属性被删除了,再找数据描述符字典里没找到,再找自己实例属性字典也没有,就报错

 

class Foo:     #创建非数据描述符做对比
    def __get__(self, instance, owner):
        print('===>get方法')


class Bar:
    x=Foo() #在何地?
    def  __getattr__(self, item):
        print('----->')

b1=Bar()
b1.x=1    #键值对已添加到实例属性底层字典当中
print(b1.__dict__)  #查看得到上面赋值的结果。如果以上加上__set__变成数据描述符方法,数据描述符优先级 > 非数据描述符,这里就没有值,变空字典了
b1.xxxxxxx    #只有当调取一个没有的实例属性,就触发找不到时的方法__getattr__,找不到的属性__getattr__()优先级最低

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值