除了语言参考手册中必读的“Data model”一章(https://docs.python.org/3/reference/datamodel.html),Raymond Hettinger 写的“Descriptor HowTo Guide”(https://docs.python.org/3/howto/descriptor.html)也值得一读——这是 Python 官方文档 HowTo 合集(https://docs.python.org/3/howto/)中的一篇。
对 Python 对象模型相关的话题来说,Alex Martelli 写的《Python 技术手册(第 2 版)》一书虽然有点过时,但仍然提供了权威且客观的论述:本章讨论的关键机制在 Python 2.2 中引入,远在那本书涵盖的 2.5 版之前。Martelli 还做了一次题为“Python's Object Model”的演讲,深入探讨了特性和描述符 [ 幻灯片(http://www.aleax.it/Python/nylug05_om.pdf),视频(https://www.youtube.com/watch?v=VOzvpHoYQoo)],强烈推荐观看。
至于针对 Python 3 的实例,David Beazley 与 Brian K. Jones 的《Python Cookbook(第 3 版)中文版》一书中有很多说明描述符的诀窍,推荐阅读的有“6.12 读取嵌套型和大小可变的二进制结构”“8.10 让属性具有惰性求值的能力”“8.13 实现一种数据模型或类型系统”和“9.9 把装饰器定义成类”。最后一个诀窍解决了函数装饰器、描述符和方法之间相互作用的深层次问题,说明了如何使用有 __call__ 方法的类实现函数装饰器;如果既想装饰方法又想装饰函数,还要实现 __get__ 方法。
杂谈
self的问题“变糟更好”(“Worse is Better”)是Richard P. Gabriel 在“The Rise of Worse is Better”一文(http://dreamsongs.com/RiseOfWorseIsBetter.html)中提出的设计思想。这个思想的第一要义是“简单”;对此,Gabriel 说道:
设计方式必须简单,对实现和接口来说都应如此。简单的实现比简单的接口更重要。简单是设计过程中最重要的考虑因素。
我认为,Python 要求明确把方法的第一个参数声明为
self是“变糟更好”思想的体现。这样,实现是简单了(甚至也优雅了),但却牺牲了用户接口:方法的签名——例如def zfill(self, width):——在外观上与pobox.zfill(8)调用不匹配。这种做法(以及使用
self这个标识符)由 Modula-3 语言创造,但是与 Python 有差异:在 Modula-3 中,接口的声明与实现是分开的,而且在接口声明中会省略self参数,因此对用户来说,接口声明中的方法显示的参数数量与真正接受的参数数量完全一致。在这方面,Python 有一项改进——错误消息。对于用户定义的单参数(除
self之外)方法来说,如果用户调用obj.meth(),Python 2.7 会抛出异常,显示TypeError: meth() takes exactly 2 arguments (1 given);不过在 Python 3.4 中,错误消息没那么难以理解了,解决了参数数量问题,还指出了缺失的参数:meth() missing 1 required positional argument: 'x'。除了要明确把
self作为参数之外,限制必须使用self访问实例属性也备受批评。8 我自己并不介意输入self限定符,这样便于把局部变量和属性区分开。我介意的是在def语句中使用self。但是我已经习惯了。如果讨厌 Python 要求显式使用
self,可以想想 JavaScript 中隐式的this那变幻莫测的语义,这样感觉就会好多了。像这样使用self有一些合理之处,Guido 在他的博客 The History of Python 中写了一篇文章,题为“Adding Support for User-defined Classes” (http://python-history.blogspot.com.br/2009/02/adding-support-for-user-defined-classes.html),说明了这些原因。
8例如,A. M. Kuchling 发表的著名文章“Python Warts”(存档:http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html)。Kuchling 自己并不讨厌 self 限定符,但是他提到了这一点——可能是为了呼应 comp.lang.python 邮件列表中的观点。