転載(https://www.cnblogs.com/Jimmy1988/p/6808237.html)Python描述符 (descriptor) 详解 作者:Jimmy_Nie

本文详细介绍了Python中的描述符机制,包括描述符的工作原理、如何重写属性访问的方法(如get()、set()和delete()),以及实例和类属性之间的交互方式。通过具体的代码示例,读者可以更直观地理解描述符的应用场景。

Python描述符 (descriptor) 详解
1、什么是描述符?

  python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 get(), set(), 和delete()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。

  以上为官方定义,纯粹为了装逼使用,一般人看这些定义都有一种问候祖先的冲动!

  没关系,看完本文,你就会理解什么叫描述符了!

2、讲解描述符前,先看一下属性:dict (每个对象均具备该属性)

作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}

对象属性的访问顺序:

①.实例属性

②.类属性

③.父类属性

④.getattr()方法

以上顺序,切记切记!

按 Ctrl+C 复制代码

class Test(object):
cls_val = 1
def init(self):
self.ins_val = 10

t=Test()
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 1, ‘init‘:

更改实例t的属性cls_val,只是新增了该属性,并不影响类Test的属性cls_val

t.cls_val = 20
t.dict
{‘ins_val’: 10, ‘cls_val’: 20}
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 1, ‘init‘:

更改了类Test的属性cls_val的值,由于事先增加了实例t的cls_val属性,因此不会改变实例的cls_val值(井水不犯河水)

Test.cls_val = 30
t.dict
{‘ins_val’: 10, ‘cls_val’: 20}
Test.dict
mappingproxy({‘module‘: ‘main‘, ‘cls_val’: 30, ‘init‘:

代码 1

class Desc(object):

def __get__(self, instance, owner):
    print("__get__...")
    print("self : \t\t", self)
    print("instance : \t", instance)
    print("owner : \t", owner)
    print('='*40, "\n")

def __set__(self, instance, value):
    print('__set__...')
    print("self : \t\t", self)
    print("instance : \t", instance)
    print("value : \t", value)
    print('='*40, "\n")


class TestDesc(object):
    x = Desc()

以下为测试代码

t = TestDesc()
t.x

以下为输出信息:

get
self : <main.Desc object at 0x0000000002B0B828>
instance : <main.TestDesc object at 0x0000000002B0BA20>

owner :

代码 2

class Desc(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print("__get__...")
        print('name = ',self.name) 
        print('='*40, "\n")

class TestDesc(object):
    x = Desc('x')
    def __init__(self):
        self.y = Desc('y')

以下为测试代码
t = TestDesc()
t.x
t.y

以下为输出结果:
get

name = x

咦,为啥没打印 t.y 的信息呢?

因为没有访问 get() 方法啊,哈哈,那么为啥没有访问 get() 方法呢?(问题真多)

因为调用 t.y 时刻,首先会去调用TestDesc(即Owner)的 getattribute() 方法,该方法将 t.y 转化为TestDesc.dict[‘y’].get(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,所以,只能忽略了。

问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整?

    答:还是让解释器来解释一下吧。

代码 3

    class Desc(object):
        def __init__(self, name):
            self.name = name
            print("__init__(): name = ",self.name)

        def __get__(self, instance, owner):
            print("__get__() ...")
            return self.name

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

    class TestDesc(object):
        _x = Desc('x')
        def __init__(self, x):
            self._x = x

以下为测试代码
t = TestDesc(10)
t._x

输入结果
init(): name = x
get() …

不对啊,按照惯例,t._x 会去调用 getattribute() 方法,然后找到了 实例t 的 _x 属性就结束了,为啥还去调用了描述符的 get() 方法呢?

    这就牵扯到了一个查找顺序问题:当Python解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。

    不信?来看一下 字典 :

复制代码
1 >>> t.dict
2 {}
3
4 >>> TestDesc.dict
5 mappingproxy({‘module‘: ‘main‘, ‘_x’: <main.Desc object at 0x0000000002B0BA20>, ‘init‘:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值