python 面向对象专题(九):特殊方法 (二)__get__、__set__、__delete__ 描述符(二)覆盖型与非覆盖型描述符对比
前言
根据是否定义__set__ 方法,描述符可分为两大类。
实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作。
没有实现 __set__ 方法的描述符是非覆盖型描述符。如果设置了同名的实例属性,描述符会被遮盖,致使描述符无法处理那个实例的那个属性。
示例 20-8 descriptorkinds.py:几个简单的类,用于研究描述符的覆盖行为
### 辅助函数,仅用于显示 ###
def cls_name(obj_or_cls):
cls = type(obj_or_cls)
if cls is type:
cls = obj_or_cls
return cls.__name__.split('.')[-1] def display(obj):
cls = type(obj)
if cls is type:
return '<class {}>'.format(obj.__name__)
elif cls in [type(None), int]:
return repr(obj)
else:
return '<{} object>'.format(cls_name(obj))
def print_args(name, *args):
pseudo_args = ', '.join(display(x) for x in args)
print('-> {}.__{}__({})'.format(cls_name(args[0]), name, pseudo_args)) ### 对这个示例重要的类 ###
class Overriding: ➊
"""也称数据描述符或强制描述符"""
def __get__(self, instance, owner):
print_args('get', self, instance, owner) ➋
def __set__(self, instance, value):
print_args('set', self, instance, value) class OverridingNoGet: ➌
"""没有``__get__``方法的覆盖型描述符"""
def __set__(self, instance, value):
print_args('set', self, instance, value) class NonOverriding: ➍
"""也称非数据描述符或遮盖型描述符"""
def __get__(self, instance, owner):
print_args('get', self, instance, owner) class Managed: ➎
over = Overriding()
over_no_get = OverridingNoGet()
non_over = NonOverriding()
def spam(self): ➏
print('-> Managed.spam({})'.format(display(self)))
❶ 有 __get__ 和 __set__ 方法的典型覆盖型描述符。
❷ 在这个示例中,各个描述符的每个方法都调用了 print_args 函数。
❸ 没有 __get__ 方法的覆盖型描述符。
❹ 没有 __set__ 方法,所以这是非覆盖型描述符。
❺ 托管类,使用各个描述符类的一个实例。
❻ spam 方法放在这里是为了对比,因为方法也是描述符。
在接下来的几节中,我们要分析对 Managed 类及其实例做属性读写时
的行为,还会讨论所定义的各个描述符。
2 覆盖型描述符
2.1 覆盖型描述符
实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作。
示例 20-9 覆盖型描述符的行为,其中 obj.over 是 Overriding类(见示例 20-8)的实例
>>> obj = Managed() ➊
>>> obj.over ➋
-> Overriding.__get__(<Overriding object>, <Managed object>,
<class Managed>)
>>> Managed.over ➌
-> Overriding.__get__(<Overriding object>, None, <class Managed>)
>>> obj.over = 7 ➍
-> Overriding.__set__(<Overriding object>, <Managed object>, 7)
>>> obj.over ➎
-> Overriding.__get__(<Overriding object>, <Managed object>,
<class Managed>)
>>> obj.__dict__['over'] = 8 ➏
>>> vars(obj) ➐
{'over': 8}
>>> obj.over ➑
-> Overriding.__get__(<Overriding object>, <Managed object>,
<class Managed>)
❶ 创建供测试使用的 Managed 对象。
❷ obj.over 触发描述符的 __get__ 方法,第二个参数的值是托管实例 obj。
❸ Managed.over 触发描述符的 __get__ 方法,第二个参数(instance)的值是 None。
❹ 为 obj.over 赋值,触发描述符的 __set__ 方法,最后一个参数的值是 7。
❺ 读取 obj.over,仍会触发描述符的 __get__ 方法。
❻ 跳过描述符,直接通过 obj.__dict__ 属性设值。
❼ 确认值在 obj.__dict__ 属性中,在 over 键名下。
❽ 然而,即使是名为 over 的实例属性,Managed.over 描述符仍会覆盖读取 obj.over 这个操作。
2.2 没有 __get__ 方法的覆盖型描述符
示例 20-10 没有 __get__ 方法的覆盖型描述符,其中obj.over_no_get 是 OverridingNoGet 类(见示例 20-8)的实例
>>> obj.over_no_get ➊
<__main__.OverridingNoGet object at 0x665bcc>
>>> Managed.over_no_get ➋
<__main__.OverridingNoGet object at 0x665bcc>
>>> obj.over_no_get = 7 ➌
-> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 7)
>>> obj.over_no_get ➍
<__main__.OverridingNoGet object at 0x665bcc>
>>> obj.__dict__['over_no_get'] = 9 ➎
>>> obj.over_no_get ➏
9
>>> obj.over_no_get = 7 ➐
-> OverridingNoGet.__set__(<OverridingNoGet object>, <Managed object>, 7)
>>> obj.over_no_get ➑
9
❶ 这个覆盖型描述符没有 __get__ 方法,因此,obj.over_no_get 从类中获取描述符实例。
❷ 直接从托管类中读取描述符实例也是如此。
❸ 为 obj.over_no_get 赋值会触发描述符的 __set__ 方法。
❹ 因为 __set__ 方法没有修改属性,所以在此读取 obj.over_no_get获取的仍是托管类中的描述符实例。
❺ 通过实例的 __dict__ 属性设置名为 over_no_get 的实例属性。
❻ 现在,over_no_get 实例属性会遮盖描述符,但是只有读操作是如此。
❼ 为 obj.over_no_get 赋值,仍然经过描述符的 __set__ 方法处理。
❽ 但是读取时,只要有同名的实例属性,描述符就会被遮盖
3 非覆盖型描述符
示例 20-11 非覆盖型描述符的行为,其中 obj.non_over 是NonOverriding 类(见示例 20-8)的实例
>>> obj = Managed()
>>> obj.non_over ➊
-> NonOverriding.__get__(<NonOverriding object>, <Managed object>,
<class Managed>)
>>> obj.non_over = 7 ➋
>>> obj.non_over ➌
7
>>> Managed.non_over ➍
-> NonOverriding.__get__(<NonOverriding object>, None, <class Managed>)
>>> del obj.non_over ➎
>>> obj.non_over ➏
-> NonOverriding.__get__(<NonOverriding object>, <Managed object>,
<class Managed>)
❶ obj.non_over 触发描述符的 __get__ 方法,第二个参数的值是obj。
❷ Managed.non_over 是非覆盖型描述符,因此没有干涉赋值操作的__set__ 方法。
❸ 现在,obj 有个名为 non_over 的实例属性,把 Managed 类的同名描述符属性遮盖掉。
❹ Managed.non_over 描述符依然存在,会通过类截获这次访问。
❺ 如果把 non_over 实例属性删除了……
❻ 那么,读取 obj.non_over 时,会触发类中描述符的 __get__ 方法;但要注意,第二个参数的值是托管实例。
在上述几个示例中,我们为几个与描述符同名的实例属性赋了值,结果依描述符中是否有 __set__ 方法而有所不同。
依附在类上的描述符无法控制为类属性赋值的操作。其实,这意味着为类属性赋值能覆盖描述符属性
4 在类中覆盖描述符
不管描述符是不是覆盖型,为类属性赋值都能覆盖描述符。这是一种猴子补丁技术,不过在示例 20-12 中,我们把描述符替换成了整数,这其实会导致依赖描述符的类不能正确地执行操作。
示例 20-12 通过类可以覆盖任何描述符
>>> obj = Managed() ➊
>>> Managed.over = 1 ➋
>>> Managed.over_no_get = 2
>>> Managed.non_over = 3
>>> obj.over, obj.over_no_get, obj.non_over ➌
(1, 2, 3)
❶ 为后面的测试新建一个实例。
❷ 覆盖类中的描述符属性。
❸ 描述符真的不见了。
示例 20-12 揭示了读写属性的另一种不对等:读类属性的操作可以由依附在托管类上定义有 __get__ 方法的描述符处理,但是写类属性的操作不会由依附在托管类上定义有 __set__ 方法的描述符处理。
python 面向对象专题(九):特殊方法 (二)__get__、__set__、__delete__ 描述符(二)覆盖型与非覆盖型描述符对比的更多相关文章
- python 面向对象进阶之内置方法
一 isinstance(obj,cls)和issubclass(sub,super) 1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(obj ...
- python基础----再看property、描述符(__get__,__set__,__delete__)
一.再看property 一个静态属性property ...
- 描述符__get__,__set__,__delete__和析构方法__del__
描述符__get__,__set__,__delete__ 1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一 ...
- Python描述符(__get__,__set__,__delete__)简介
先说定义,这里直接翻译官方英文文档: 一般来说,描述符是具有“绑定行为”的对象属性,该对象的属性访问将会被描述符协议中的方法覆盖.这些方法是__get__(),__set__(),和__delete_ ...
- __get__ __set__ __delete__描述符
描述符就是一个新式类,这个类至少要实现__get__ __set__ __delete__方法中的一种class Foo: def __get__(self, instance, owner): pr ...
- 描述符__get__(),__set__(),__delete__()(三十七)
http://www.cnblogs.com/linhaifeng/articles/6204014.html#_label12 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__ ...
- 描述符__get__,__set__,__delete__
描述符__get__,__set__,__delete__ # 描述符:1用来代理另外一个类的属性 # __get__():调用一个属性时,触发 # __set__():为一个属性赋值时触发 # __ ...
- python 面向对象专题(十):特殊方法 (三)__get__、__set__、__delete__ 描述符(三)方法是描述符
在类中定义的函数属于绑定方法(bound method),因为用户定义的函数都有 __get__ 方法,所以依附到类上时,就相当于描述符.示例 20-13 演示了从 面向对象专题(九)示例 20-8 ...
- python 面向对象专题(八):特殊方法 (一)__get__、__set__、__delete__ 描述符(一)
https://www.cnblogs.com/flashBoxer/p/9771797.html 实现了 __get__.__set__ 或 __delete__ 方法的类是描述符.描述符的用法是, ...
随机推荐
- JVM内存结构详解
从java编程语言说起... 1. Java编程语言简介 1.1 编程语言概述 系统级和应用级 系统级:C,C++,go,erlang 应用级:C#,Java,Python,Perl,Ruby,php ...
- c++ vector基本函数、排序、查找用法
vector用法目录: 1.基本用法 2.vector的删除操作 3.vector的sort排序 4.翻转vector中的所有元素 5.find()函数的用法 6.vector实战(这里写的是我在最开 ...
- TensorFlow从0到1之矩阵基本操作及其实现(7)
矩阵运算,例如执行乘法.加法和减法,是任何神经网络中信号传播的重要操作.通常在计算中需要随机矩阵.零矩阵.一矩阵或者单位矩阵. 本节将告诉你如何获得不同类型的矩阵,以及如何对它们进行不同的矩阵处理操作 ...
- sourcetree 安装破解注册方法
1.下载sourcetree安装包 2.点击安装到下图步骤 3.在网盘中下载accounts.json 文件,( 链接:https://pan.baidu.com/s/1tJd_xCh-B-oOwd ...
- 龙芯团队完成CoreCLR MIPS64移植,在github开源
国产龙芯的软件生态之中.NET不会缺席,毕竟 C# 与 .NetCore/Mono 也是全球几大主流的编程语言和运行平台之一,最近一段时间听到太多的鼓吹政务领域不支持.NET, 大家都明白这是某些人为 ...
- 【解读】Https协议
一.为什么需要https 1.HTTP是明文传输的,也就意味着,介于发送端.接收端中间的任意节点都可以知道你们传输的内容是什么.这些节点可能是路由器.代理等. 举个最常见的例子,用户登陆.用户输入账号 ...
- 尚硅谷ajax视频教程2
7.7. 尚硅谷_佟刚_Ajax_典型应用_验证用户名是否可用 整个项目的目录路径如下所示 我们首先新建立一个web工程,在webroot下面新建立一个script的文件夹,导入jquer文件 接下来 ...
- curl模拟调用接口
curl模拟调用接口 1. get请求 curl -i -X GET http://url/bind/agentOnWork/v2?Sig=******* 2. post请求(带头信息以及参数) cu ...
- Java中时间加减的比较
public class TestDate{ public static void main(String[] args){try{ Date date=new Date(); DateFormat ...
- 小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了
这里涉及到一个关键词:策略模式,那么到底什么是策略模式呢?本文就来好好给大家讲讲策略模式,大家可以带着如下几个问题来阅读本文: 1. 如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考) ...