Python的描述器Descriptors

一、描述器的表现

用到3个魔术方法: get() 、 set() 、 delete()
方法签名如下:
object.get(self, instance, owner)
object.set(self, instance, value)
object.delete(self, instance)
self 指代当前实例,调用者 ,instance 是owner的实例 ,owner 是属性的所属的类、也叫属主;

1、描述器的定义

Python中,一个类实现了 getsetdelete 三个方法中的任何一个方法,就是描述器,即描述器的类;要想把描述器用起来需把它赋给一个类属性,如果把它赋给实例没有效果、不会触发描述器机制、即不会触发__get__,这套机制就是这么设计的;
如果仅实现了 get ,就是非数据描述符 non-data descriptor;
同时实现了 getset 就是数据描述符 data descriptor;
如果一个类的类属性设置为描述器实例,那么它被称为owner属主

2、程序执行流程

类加载的时候,类变量需要先生成,而类B的x属性是类A的实例,所以类A先初始化,所以打印A.init;然后执行到打印B.x.a1;然后实例化并初始化B的实例b, 打印b.x.a1,会查找类属性b.x,指向A的实例,所以返回A实例的属性a1的值;
一旦通过访问类属性x,即访问A(),就会被__get__方法先拦截;要注意分析instance,是否触发描述器机制、即__get__方法,以及返回值;这算是一种黑魔法,因为通过属性描述器就能操作属主的类,功能强大;

class A:
    def __init__(self):
        self.a1 = 'a1'
        print('A.init')
​
class B:
    x = A()
    def __init__(self):
        print('B.init')
​
print('-'*20)
print(B.x.a1)
print('='*20)
b = B()
print(b.x.a1)
​
​
# 运行结果
A.init
--------------------
a1
====================
B.init
a1

3、描述器

因为定义了 get 方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问, 就会调用 get 方法;

# 类A中实现 __get__ 方法
# 类调用描述器和实例调用描述器的区别
class A:
    def __init__(self):
        self.a1 = 'a1'
        print('A.init')
​
    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self, instance, owner))
​
class B:
    x = A()
    def __init__(self):
        print('B.init')
​
print('-'*20)
print(B.x)   # 调用__get__方法,但instance为None,因为是类B调用x属性、并不是类B的实例
#print(B.x.a1) # 抛异常AttributeError: 'NoneType' object has no attribute 'a1'
​
print('='*20)
b = B()
print(b.x)  # 调用__get__方法,instance为b
#print(b.x.a1) # 抛异常AttributeError: 'NoneType' object has no attribute 'a1'
​
​
# 运行结果,抛异常AttributeError原因是__get__方法返回值为None
A.init
--------------------
A.__get__ <__main__.A object at 0x103e2aa20> None <class '__main__.B'>
None
====================
B.init
A.__get__ <__main__.A object at 0x103e2aa20> <__main__.B object at 0x103e2ab00> <class '__main__.B'>
None




# 类A中实现 __get__ 方法,解决__get__返回问题
class A:
    def __init__(self):
        self.a1 = 'a1'
        print('A.init')
​
    def __get__(self, instance, owner):
        print("A.__get__ {} {} {}".format(self, instance, owner))
        return self   # 解决返回None的问题,返回一个A的实例
​
class B:
    x = A()
    def __init__(self):
        print('B.init')
​
print('-'*20)
print(B.x)   # 调用__get__
print(B.x.a1)   # 调用__get__
​
print('='*20)
b = B()
print(b.x)   # 调用__get__
print(b.x.a1)   # 调用__get__
​
​
# 运行结果
A.init
--------------------
A.__get__ <__main__.A object at 0x10c439a20> None <class '__main__.B'>
<__main__.A object at 0x10c439a20>
A.__get__ <__main__.A object at 0x10c439a20> None <class '__main__.B'>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值