- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
9.11 延伸阅读
本章介绍了数据模型的几个特殊方法,因此主要参考资料与第 1 章一样,阅读那些资料能对这个话题有个整体了解。方便起见,我再次给出之前推荐的四个资料,同时再多加几个。
Python 语言参考手册中的“Data Model”一章
本章用到的方法大部分见于“3.3.1. Basic customization”。
《Python 技术手册(第 2 版)》,Alex Martelli 著
虽然这本书只涵盖 Python 2.5(第 2 版),但是对数据模型做了深入说明。基本的概念都是一样的,而且自 Python 2.2 起(这一版的内置类型和用户定义的类兼容性变得更好),数据模型的大多数 API 完全没变。
《Python Cookbook(第 3 版)中文版》,David Beazley 和 Brian K. Jones 著
通过诀窍来演示现代化的编程实践。尤其是第 8 章“类与对象”,其中有好几个方案与本章讨论的话题有关。
《Python 参考手册(第 4 版)》,David Beazley 著
详细说明了 Python 2.6 和 Python 3 的数据模型。
本章涵盖了与对象表示形式有关的全部特殊方法,唯有 __index__ 除外。这个方法的作用是强制把对象转换成整数索引,在特定的序列切片场景中使用,以及满足 NumPy 的一个需求。在实际编程中,你我都不用实现 __index__ 方法,除非决定新建一种数值类型,并想把它作为参数传给 __getitem__ 方法。如果好奇的话,可以阅读 A.M.Kuchling 写的“What's New in Python 2.5”,这篇文章做了简要说明;此外,还可以阅读“PEP 357—Allowing Any Object to be Used for Slicing”,这份 PEP 从 C 语言扩展的实现者和 NumPy 的作者 Travis Oliphant 的角度详述了对 __index__ 方法的需求。
意识到应该区分字符串表示形式的早期语言是 Smalltalk。1996 年,Bobby Woolf 写了一篇题为“How to Display an Object as a String: printString and displayString”的文章,他在这篇文章中讨论了 Smalltalk 对 printString 和 displayString 方法的实现。在 9.1 节说明 repr() 和 str() 的作用时,我从这篇文章中借用了言简意赅的表述,即“便于开发者理解的方式”和“便于用户理解的方式”。
杂谈
特性有助于减少前期投入
在 Vector2d 类的第一版中,x 和 y 属性是公开的;默认情况下,Python 的所有实例属性和类属性都是公开的。这对向量来说是合理的,因为我们要能访问分量。虽然这些向量是可迭代的对象,而且可以拆包成一对变量,但是还要能够通过 my_vector.x 和 my_vector.y 获取各个分量。
如果觉得应该避免意外更新 x 和 y 属性,可以实现特性,但是代码的其他部分没有变化,Vector2d 的公开接口也不受影响,这一点从 doctest 中可以得知。我们依然能够访问 my_vector.x 和 my_vector.y。
这表明我们可以先以最简单的方式定义类,也就是使用公开属性,因为如果以后需要对读值方法和设值方法增加控制,那就可以实现特性,这样做对一开始通过公开属性的名称(如 x 和 y)与对象交互的代码没有影响。
Java 语言采用的方式则截然相反:Java 程序员不能先定义简单的公开属性,然后在需要时再实现特性,因为 Java 语言没有特性。因此,在 Java 中编写读值方法和设值方法是常态,就算这些方法没做什么有用的事情也得这么做,因为 API 不能从简单的公开属性变成读值方法和设值方法,同时又不影响使用那些属性的代码。
此外,本书的技术审校 Alex Martelli 指出,到处都使用读值方法和设值方法是愚蠢的行为。如果想编写下面的代码:
--- >>> my_object.set_foo(my_object.get_foo() + 1) ---
这样做就行了:
--- >>> my_object.foo += 1 ---
维基的发明人和极限编程先驱 Ward Cunningham 建议问这个问题:“做这件事最简单的方法是什么?”意即,我们应该把焦点放在目标上。11 提前实现设值方法和读值方法偏离了目标。在 Python 中,我们可以先使用公开属性,然后等需要时再变成特性。
私有属性的安全性和保障性
Perl 不会强制你保护隐私。你应该待在客厅外,因为你没收到邀请,而不是因为里面有把枪。
——Larry Wall
Perl 之父
Python 和 Perl 在很多方面的做法是截然相反的,但是 Larry 和 Guido 似乎都同意要保护对象的隐私。
这些年我教过许多 Java 程序员学习 Python,我发现很多人都对 Java 提供的隐私保障推崇备至。可事实是,Java 的 private 和 protected 修饰符往往只是为了防止意外(即一种安全措施)。只有使用安全管理器部署应用时才能保障绝对安全,防止恶意访问;但是,实际上很少有人这么做,即便在企业中也少见。
下面通过一个 Java 类证明这一点(见示例 9-15)。
示例 9-15 Confidential.java:一个 Java 类,定义了一个私有字段,名为 secret
public class Confidential { private String secret = ""; public Confidential(String text) { secret = text.toUpperCase(); } }
在示例 9-15 中,我把 text 转换成大写后存入 secret 字段。转换成大写是为了表明 secret 字段中的值全部是大写的。
我们要使用 Jython 运行 expose.py 脚本才能真正说明问题。那个脚本使用内省(Java 称之为“反射”)获取私有字段的值。expose.py 脚本的代码在示例 9-16 中。
示例 9-16 expose.py:一段 Jython 代码,从另一个类中读取一个私有字段
import Confidential message = Confidential('top secret text') secret_field = Confidential.getDeclaredField('secret') secret_field.setAccessible(True) # 攻破防线 print 'message.secret =', secret_field.get(message)
运行示例 9-16 得到的结果如下:
$ jython expose.py message.secret = TOP SECRET TEXT
字符串 'TOP SECRET TEXT' 从 Confidential 类的私有字段 secret 中读取。
这里没有什么黑魔法:expose.py 脚本使用 Java 反射 API 获取私有字段 'secret' 的引用,然后调用 'secret_field.setAccessible(True)' 把它设为可读的。显然,使用 Java 代码也能做到这一点(不过所需的代码行数是这里的三倍多,参见本书代码仓库里的 Expose.java 文件。
如果这个 Jython 脚本或 Java 主程序( 如 Expose.class) 在 SecurityManager 的监管下运行,.setAccessible(True) 这个关键的调用就会失败。但是现实中,很少有人部署 Java 应用时会使用 SecurityManager,Java applet 除外(还记得这个吗?)。
我的观点是,Java 中的访问控制修饰符基本上也是安全措施,不能保证万无一失——至少实践中是如此。因此,安心享用 Python 提供的强大功能吧,放心去用吧!
11参见“Simplest Thing that Could Possibly Work: A Conversation with Ward Cunningham, Part V”。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论