Python 描述器 Descriptors 详解

本文详细介绍了Python中的描述器概念,包括非数据和数据描述器的区别,以及描述器在类属性访问中的作用。通过多个示例,阐述了描述器的工作原理,特别是属性查找顺序、无限递归的处理和如何防止实例属性覆盖。最后,讨论了描述器的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、概念介绍

  • 描述器 是两个类之间的事

  • 描述器一般会用到4个魔术方法,如下:

    class A:
        def __get__(self, instance, owner):
            pass
        # self      指代当前实例,调用者
        # instance  是owner的实例
        # owner     是属性的所属的类
        def __set__(self, instance, value):    
            pass
        
        def __delete__(self, instance):
            pass
        
        # 3.6以后,新增一个魔术方法
        def __set_name__(self, owner, name):
            pass
    
  • Python 中,一个类实现了__get__ 、 __set__ 、 __delete__三个方法中的任何一个方法,就是描述器。实现这三个中的某些方法,就支持了描述器协议

  • 仅实现了__get__,就是 非数据描述器 non-data descriptor

  • 实现了__get____set__,就是 数据描述器 data descriptor

  • 如果一个类的类属性设置为描述器实例,那么它就称为owner属主

  • 当该类的类属性被查找、设置和删除时,就会调用描述器相应的方法

  • 属性查找顺序:数据描述器 优先于 实例的__dict__ 优先于 非数据描述器

  • __delete__ 方法有同样的效果,有了这个方法,也是数据描述器

  • 数据描述器可以阻止实例对属性的修改

2、通过示例验证

2.1 正常的类属性访问

class A:
    def __init__(self):
        print('A init')
        self.a = 'AAA'

class B:
    x = A()
    def __init__(self):
        print('B init')
        self.a = 'BBB'
        

print(1, B.x)
print(2, B().x)
print(3, B.x.a)
print(4, B().x.a)
b1 = B()
print(5, b1.x)
print(6, b1.x.a)
print(7, b1.a)
print(8, b1.__dict__)
A init
1 <__main__.A object at 0x000001A4A049C580>
B init
2 <__main__.A object at 0x000001A4A049C580>
3 AAA
B init
4 AAA
B init
5 <__main__.A object at 0x000001A4A049C580>
6 AAA
7 BBB
8 {
   'a': 'BBB'}

2.2 非数据描述器且实例没有同名属性

# 非数据描述器
# self在哪个类中,就是哪个类的实例
# class B实例没有同名类属性
class A:
    def __init__(self):
        print('A init')
        self.a = 'AAA'

    def __get__(self, instance, owner):  # 类A 是非数据描述器
        print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
        return self

class B:
    x = A()  # 对类B或者类B的实例的属性读取,称为对类A的实例访问,就会调用__get__方法
    def __init__(self):
        print('B init')
        self.a = 'BBB'
        self.b = A()
        
print('+' * 55)
print(1, B.x)  # 调用__get__方法
print(2, B.x.a)  # 调用__get__方法
print('*' * 55)
print(3, B().x)  # 调用__get__方法
print(4, B().x.a)  # 调用__get__方法
print('=' * 55)
b1 = B()
print(5, b1.x)  # 调用__get__方法
print(6, b1.x.a)  # 调用__get__方法
print('+' * 55)
print(7, b1.b)  # 实例属性是类属性时,并不会调用 非数据描述器 的__get__方法
print(8, b1.__dict__)
A init
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is None.
owner is <class '__main__.B'>.<=]
1 <__main__.A object at 0x000001A4A23F1550>
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is None.
owner is <class '__main__.B'>.<=]
2 AAA
*******************************************************
B init
A init
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A202EFA0>.
owner is <class '__main__.B'>.<=]
3 <__main__.A object at 0x000001A4A23F1550>
B init
A init
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A202E070
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值