Python面向对象之描述器

描述器Descriptors

描述器的表现
描述器需要用到三个魔术方法:get(),set(),delete()。
方法签名如下:

  • object.get(self,instance,owner)
  • object.set(self,instance,owner)
  • object.delete(self,instance)
    self指代当前实例,调用者
    instance是owner的实例
    owner是属性的所属的类
class A:
    def __init__(self):
        self.a1='a1'
        print('A.init')
    def __get__(self, instance, owner):
        print(self,instance,owner)
class B:
    x=A()
    def __init__(self):
        print('B.init')

print(B.x)
b=B()
print(b.x)

在这里插入图片描述
定义了__get__()方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问,就会调用__get__方法
在这里插入图片描述
使用B的实例调取A的属性失败
观察self,instance,owner三个参数的含义。

get(self,instance,owner)方法的签名,会传入3个参数
B.x调用返回,<main.A object at 0x0000022363040E10> None <class ‘main.B’>
b.x调用返回,<main.A object at 0x0000022363040E10> <main.B object at 0x0000022363040E80> <class ‘main.B’>

self对用都是A的实例
owner对应都是B类
instance说明:

  • None表示不是B类的实例,对应调用B.x
  • <main.B object at 0x0000022363040E80> 表示是B的实例,对应调用B().x
    使用返回值解决,返回self,就是A的实例,该实例中有a1属性。
    类B中的实例属性:
class A:
    def __init__(self):
        self.a1='a1'
        print('A.init')
    def __get__(self, instance, owner):
        print(self,instance,owner)
        return self
class B:
    x=A()
    def __init__(self):
        print('B.init')
        self.b=A()

print(B.x)
b=B()
print(b.x)
print(b.x.a1)
print(b.b)

在这里插入图片描述
从运行结果来看,只有类属性是类的实例才行。

描述器定义
Python中,一个类实现了__get__、set、__delete__三个方法中的任何一个方法,就是描述器,实现这三个中的某些方法,就支持了描述器协议。

  • 仅实现了__get__,就是非数据描述符non-data descriptor
  • 实现了__get__、__set__就是数据描述符data descriptor
    如果一个类的类属性设置为描述器实例,那么它被称为owner属主
    当该类的该类属性被查找、设置、删除时,就会调用描述器相应的方法。

属性的访问顺序

class A:
    def __init__(self):
        self.a1='a1'
        print('A.init')
    def __get__(self, instance, owner):
        print(self,instance,owner)
        return self
class B:
    x=A()
    def __init__(self):
        print('B.init')
        self.x='b.x'
print(B.x)
print(B.x.a1)
b=B()
print(b.x)
print(b.x.a1)
print(b.x)

在这里插入图片描述
类A只实现了__get__()方法,b.x访问到了实例的属性,而不是描述器

class A:
    def __init__(self):
        self.a1='a1'
        print('A.init')
    def __get__(self, instance, owner):
        print(self,instance,owner,'---get---')
        return self
    def __set__(self, instance, value,):
        print(self,instance,value,'---set---')
class B:
    x=A()
    def __init__(self):
        print('B.init')
        self.x='b.x.1'
print(B.x)
print(B.x.a1)
b=B()
print(b.x)
print(b.x.a1)
print(b.x)

在这里插入图片描述
所有b.x就会访问__get__()方法,代码中返回self就是描述器实例
属性查找顺序:
实例的__dict__优先于非数据描述器
数据描述器优先于实例的__dict__
delete__方法有同样的效果,有了这个方法们也是数据描述器
Python中的描述器
描述器在Python中应用非常广泛
Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器,因此,实例可以重新定义和覆盖方法,这允许单个实例获取与同一类的其他实例不同的行为。
property()函数实现为一个数据描述器,因此,实例不能覆盖属性的行为。
新增方法
3.6新增描述器方法__set_name
,它在属主类构建的时候就会调用

class A:
    def __init__(self):
        self.a1='a1'
        print('A.init')
    def __get__(self, instance, owner):
        print(self,instance,owner,'get')
        return self
    def __set_name__(self,owner,name):
        print(self,owner,name,'name')
class B:
    x=A()

在这里插入图片描述
提供这个方法,就是可以知道属主类和属主类的类属性名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值