python descriptor 官方文档指引[翻译]

翻译自官方文档描述符指引

摘要

定义描述符,概述描述符的协议,并且展示描述符如何被调用。检查自定义描述符和几个内置的python描述符(包括函数、属性,静态方法和类方法)。通过一段python示例程序去展示描述符是怎么执行的。

学习描述符不仅提供了接触更多工具的机会,也会对python运行机制和设计优雅的代码有更深入的理解。


定义和介绍

一般来说,一个描述符是一个绑定的对象属性,它能够被描述符里的方法覆盖。这样的方法有__get__(), __set__() 和__delete__()。如果有一个对象定义了这些方法,那就可以称作一个描述符。

访问属性的默认行为是从一个对象的字典中get,set或delete这个属性。例如,a.x有一个以a.__dict__['x']开头的查找链,然后是type(a).__dict__['x'],最后是除了元类的type(a) 的基类。如果查找的值是描述符方法定义的对象,那么python就会重写默认的行为,并且调用描述符的方法。在优先级链中发生的位置取决于定义了哪些描述符方法。注意,描述符仅仅被调用用于新式对象或类(类继承自object或者type)。

描述符是一个强大的通用协议。它们是属性,方法,静态方法,类方法和super()背后的机制。 它们被用于整个Python本身以实现在2.2版本中引入的新风格类。描述符简化了底层C代码,为日常Python程序提供了一套灵活的新工具。


描述符协议

descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

以上就是所有的描述符协议,一个对象定义了任何以上的方法都可以视为描述符,并且可以在查找属性时重写默认的行为。

如果一个对象定义了__get()__和__set__(),这个对象就可视为一个数据描述符。如果只定义了__get__()就被称为非数据描述符。

数据和非数据描述符的区别在于如何覆盖对于实例的字典中的条目计算。如果实例的字典具有与数据描述符相同名称的条目,则数据描述符优先。如果实例的字典具有与非数据描述符相同名称的条目,则字典条目优先。

实现一个只读的数据描述符,需要用到__get__()和__set__(),当这个描述符被调用的时候__set__()抛出一个AttributeError,用一个抛出异常的占位去定义__set__()方法足以使之成为数据描述符。


获取描述符

一个描述符可以被方法名直接调用,例如,d.__get__(obj).

另外一种更常见的是在访问属性的时候自动调用描述符。例如,obj.d是在obj的字典中查询d。如果d定义了__get__()方法,则根据下面列出的优先级规则调用d .__get__(obj)。

调用的细节取决于obj是一个对象还是一个类。无论哪种方式,描述符只适用于新样式对象和类。新式类是他的子类继承于object。

对于对象,这构造是在object.__getattribute__()中转换b.x为type(b).__dict__['x'].__get__(b, type(b))。该实现通过一个数据描述符>实例变量>非数据描述符的优先链,并且如果__getattr__()存在,就赋予它最低优先级,完整的c语言实现可以在object.c中找到。

对于类,这构造是在type.__getattribute__()中转换B.x为B.__dict__['x'].__get__(b, type(b))。用python实现如下:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
        return v.__get__(None, self)
    return v
(占坑更新。。。)


1. 开胃菜 2. 使用 Python 解释器 2.1. 调用 Python 解释器 2.1.1. 参数传递 2.1.2. 交互模式 2.2. 解释器及其环境 2.2.1. 错误处理 2.2.2. 执行 Python 脚本 2.2.3. 源程序编码 2.2.4. 交互执行文件 2.2.5. 本地化模块 3. Python 简介 3.1. 将 Python 当做计算器 3.1.1. 数字 3.1.2. 字符串 3.1.3. 关于 Unicode 3.1.4. 列表 3.2. 编程的第一步 4. 深入 Python 流程控制 4.1. if 语句 4.2. for 语句 4.3. The range() 函数 4.4. break 和 continue 语句, 以及循环中的 else 子句 4.5. pass 语句 4.6. 定义函数 4.7. 深入 Python 函数定义 4.7.1. 默认参数值 4.7.2. 关键字参数 4.7.3. 可变参数列表 4.7.4. 参数列表的分拆 4.7.5. Lambda 形式 4.7.6. 文档字符串 4.8. 插曲:编码风格 5. 数据结构 5.1. 关于列表更多的内容 5.1.1. 把链表当作堆栈使用 5.1.2. 把链表当作队列使用 5.1.3. 列表推导式 5.1.4. 嵌套的列表推导式 5.2. del 语句 5.3. 元组和序列 5.4. 集合 5.5. 字典 5.6. 循环技巧 5.7. 深入条件控制 5.8. 比较序列和其它类型 6. 模块 6.1. 深入模块 6.1.1. 作为脚本来执行模块 6.1.2. 模块的搜索路径 6.1.3. “编译的” Python 文件 6.2. 标准模块 6.3. dir() 函数 6.4. 包 6.4.1. 从 * 导入包 6.4.2. 包内引用 6.4.3. 多重目录中的包 7. 输入和输出 7.1. 格式化输出 7.1.1. 旧式的字符串格式化 7.2. 文件读写 7.2.1. 文件对象方法 7.2.2. pickle 模块 8. 错误和异常 8.1. 语法错误 8.2. 异常 8.3. 异常处理 8.4. 抛出异常 8.5. 用户自定义异常 8.6. 定义清理行为 8.7. 预定义清理行为 9. 类 9.1. 术语相关 9.2. Python 作用域和命名空间 9.2.1. 作用域和命名空间示例 9.3. 初识类 9.3.1. 类定义语法 9.3.2. 类对象 9.3.3. 实例对象 9.3.4. 方法对象 9.4. 一些说明 9.5. 继承 9.5.1. 多继承 9.6. 私有变量 9.7. 补充 9.8. 异常也是类 9.9. 迭代器 9.10. 生成器 9.11. 生成器表达式 10. Python 标准库概览 10.1. 操作系统接口 10.2. 文件通配符 10.3. 命令行参数 10.4. 错误输出重定向和程序终止 10.5. 字符串正则匹配 10.6. 数学 10.7. 互联网访问 10.8. 日期和时间 10.9. 数据压缩 10.10. 性能度量 10.11. 质量控制 10.12. “瑞士军刀” 11. 标准库浏览 – Part II 11.1. 输出格式 11.2. 模板 11.3. 使用二进制数据记录布局 11.4. 多线程 11.5. 日志 11.6. 弱引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值