鱼C论坛

 找回密码
 立即注册
查看: 760|回复: 0

[学习笔记] 描述符

[复制链接]
发表于 2023-4-2 16:20:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
>>> #通过一个典型的例子说明,描述符只能应用于类属性,而不能应用于对象属性。
        描述符分为数据描述符和非数据描述符,
        前者是指实现了__set__()或__delete__()中任意一个方法的描述符,
        后者是指仅实现了__get__()方法的描述符。
        当发生属性访问时,数据描述符、实例对象属性、非数据描述符、类属性的优先级从高到低依次递减。
        事实上,该优先级顺序取决于管理属性获取的__getattribute__()方法的默认实现逻辑,重写该方法有望打破上述访问优先级的“碾压”。
# Python3.6为描述符新添加了除__get__()、__set__()、__delete__()之外的
        第四个魔法方法——__set_name__(),用于捕获被描述符拦截的属性名,可使代码更为优雅,符合Python程序员一贯遵循的简洁易读的编程理念!
>>> class D:
...         def __get__(self, instance, owner):
...                 print('get~~')
...
>>> class C:
...         def __init__(self):
...                 self.x = D()
...
>>> c = C()
>>> print(c.x)
<__main__.D object at 0x0216E1F0>
>>> #描述符只能应用于类属性,而这里把它应用于对象属性,使用不会打印get~

>>> class C:
...         x = D()
...
>>> c = C()
>>> c.x
get~~
>>> # 数据描述符:实现__set__()和__delete__()中的一个
# 非数据描述符:仅实现__get__()方法
# 当发生属性访问的时候,优先级从高到低依次是:数据描述符 -> 实例对象属性 -> 非数据描述符 -> 类属性
>>> c.x = 'fishc'
>>> print(c.x)
fishc

>>> # D为非数据描述符,优先级小于实例对象属性,所以可以更改实例对象的属性
>>> C.x
get~~
>>> class D:
...         def __get__(self, instance, owener):
...                 print('get~~')
...         def __set__(self, instance, value):
...                 print('set~~')
...
>>> class C:
...         x = D()
...
>>> c = C()
>>> c.x = 'FishC'
set~~
>>> c.x
get~~
>>> print(c.__dict__)
{}
>>> # 数据描述符优先级大于实例对象属性
>>> c.__dict__['x'] = 'FishC'
>>> print(c.__dict__)
{'x': 'FishC'}
>>> c.x
get~~

>>> # 拥有__set__()方法后D为数据描述符,访问对象属性依然执行优先级较高的__get__()
>>> # 优先级是定义在__getattribute__()魔法方法实现,因为其管理的是属性的获取
>>> class C:
...         x = D()
...         def __getattribute__(self, item):
...                 print('aha~~')
...
>>> c = C()
>>> c.x
aha~~
>>> # 没有执行描述符D中的__get__()魔法方法
>>> #__set_name__(self, name, owner):描述符的第四个魔法方法 ptyhon3.6。
        # 前边的描述符的魔法方法__get__()、__set__()、__delete__()实现的都是对属性的拦截,
        在实际开发工作中,我们通过描述符去拦截一个属性,然后做了一些额外的工作,之后通常还要执行相应的访问、赋值或删除等操作。
        # 如我们通过描述符拦截了对象的x属性,并进行了合法性验证,判断他的值是不是大于某个值之类的操作,如果符合要求我们仍然要把它写入到实例对象的属性中去。
        也就是说我们需要在描述符里去操作实例对象的__dict__()字典,
        这其实是可以做到的,因为instance参数代表的就是描述符所拦截的属性所在的实例对象。
>>> class D:
...         def __init__(self, name):
...                 self.name = name #name的作用就是记住这个描述符的属性名
...         def __get__(self, instance, owner):
...                 print('get~~')
...                 return instance.__dict__.get(self.name)
...         def __set__(self, instance, value):
...                 print('set~~')
...                 instance.__dict__[self.name] = value
...
>>> class C:
...         x = D('x')
...
>>> c = C()
>>> c.x
get~~

>>> #__get__()魔法方法拦截成功,此时属性x里没有内容,所以没有返回
>>> print(c.__dict__)
{}
>>> c.x = 250
set~~
>>> print(c.__dict__)
{'x': 250}
>>> print(c.x)
get~~
250

>>> # class C中代码x = D('x')不够优雅,传入的属性x是以字符串'x'的形式传入。
>>> class D:
...         def __set_name__(self, owner, name):
...                 self.name = name # name 作用记住这个描述符的属性名
...         def __get__(self, instance, owner):
...                 print('get~~')
...                 return instance.__dict__.get(self.name)
...         def __set__(self, instance, value):
...                 print('set~~')
...                 instance.__dict__[self.name] = value
...
>>> class C:
...         x = D()
...
>>> c = C()
>>> c.x
get~~
>>> print(c.__dict__)
{}
>>> c.x = 250
set~~
>>> print(c.__dict__)
{'x': 250}
>>> print(c.x)
get~~
250
>>>
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 13:20

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表