返回介绍

9.11 延伸阅读

发布于 2024-02-05 21:59:47 字数 4542 浏览 0 评论 0 收藏 0

本章介绍了数据模型的几个特殊方法,因此主要参考资料与第 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文