前言

根据是否定义__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__ 描述符(二)覆盖型与非覆盖型描述符对比的更多相关文章

  1. python 面向对象进阶之内置方法

    一 isinstance(obj,cls)和issubclass(sub,super) 1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(obj ...

  2. python基础----再看property、描述符(__get__,__set__,__delete__)

    一.再看property                                                                          一个静态属性property ...

  3. 描述符__get__,__set__,__delete__和析构方法__del__

    描述符__get__,__set__,__delete__ 1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一 ...

  4. Python描述符(__get__,__set__,__delete__)简介

    先说定义,这里直接翻译官方英文文档: 一般来说,描述符是具有“绑定行为”的对象属性,该对象的属性访问将会被描述符协议中的方法覆盖.这些方法是__get__(),__set__(),和__delete_ ...

  5. __get__ __set__ __delete__描述符

    描述符就是一个新式类,这个类至少要实现__get__ __set__ __delete__方法中的一种class Foo: def __get__(self, instance, owner): pr ...

  6. 描述符__get__(),__set__(),__delete__()(三十七)

    http://www.cnblogs.com/linhaifeng/articles/6204014.html#_label12 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__ ...

  7. 描述符__get__,__set__,__delete__

    描述符__get__,__set__,__delete__ # 描述符:1用来代理另外一个类的属性 # __get__():调用一个属性时,触发 # __set__():为一个属性赋值时触发 # __ ...

  8. python 面向对象专题(十):特殊方法 (三)__get__、__set__、__delete__ 描述符(三)方法是描述符

    在类中定义的函数属于绑定方法(bound method),因为用户定义的函数都有 __get__ 方法,所以依附到类上时,就相当于描述符.示例 20-13 演示了从 面向对象专题(九)示例 20-8 ...

  9. python 面向对象专题(八):特殊方法 (一)__get__、__set__、__delete__ 描述符(一)

    https://www.cnblogs.com/flashBoxer/p/9771797.html 实现了 __get__.__set__ 或 __delete__ 方法的类是描述符.描述符的用法是, ...

随机推荐

  1. mysql小数类型

    原文链接:https://blog.csdn.net/weixin_42047611/article/details/81449663 MySQL 中使用浮点数和定点数来表示小数. 浮点类型有两种,分 ...

  2. 《MySQL技术内幕:InnoDB存储引擎》读书笔记

    一.Mysql体系结构和存储引擎 1. 概念:              数据库:物理操作系统文件或其他形式文件类型的集合.(是文件的集合,是依照某种数据模型组织起来并存放于二级存储器中的数据集合.) ...

  3. upd套接字服务器与客户端交互C++代码示范

    upd套接字服务器与客户端交互C++代码示范 // vc2_2_4UDPserver_Txwtech.cpp : 定义控制台应用程序的入口点. //服务器端 #include "StdAfx ...

  4. 【原创】强撸基于 .NET 的 Redis Cluster 集群访问组件

    Hello 大家好,我是TANZAME,我们又见面了.今天我们来聊聊怎么手撸一个 Redis Cluster 集群客户端,纯手工有干货,您细品. 随着业务增长,线上环境的QPS暴增,自然而然将当前的单 ...

  5. JDBC——使用JDBC连接MySQL数据库

    在JDBC--什么是JDBC一文中我们已经介绍了JDBC的基本原理. 这篇文章我们聊聊如何使用JDBC连接MySQL数据库. 一.基本操作 首先我们需要一个数据库和一张表: CREATE DATABA ...

  6. Spring boot+Mybatisplus用AR模式实现逻辑删除操作

    Mybatisplus的AR模式 Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录.ActiveRecord ...

  7. 解决 React Native Android:app:validateSigningRelease FAILED 错误

    RN 运行的时候报这个错这咋办:

  8. springboot项目打war包发布到外置tomcat

    第一步:修改pom.xml 1. <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> ...

  9. Vue前端压缩图片

    一.在组件包下新建compressImage.js // 压缩图片 // eslint-disable-next-line no-unused-vars export function compres ...

  10. 痞子衡嵌入式:kFlashFile v1.0 - 一个基于Flash的掉电数据存取方案

    大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的个人小项目 - kFlashFile. 痞子衡最近在参与一个基于 i.MXRT1170 的项目,项目有个需求,需要在 Flash 里实 ...