The History of Python

2009年4月28日星期二

一切都是一类公民

英文原文链接:http://python-history.blogspot.com/2009/02/first-class-everything.html

原文作者:Guido van Rossum

[朋友们,请不要在这个Blog的评论部分提问题。如果你想建议这个Blog将来写些什么,给我发email(用Google可以搜到我的主页,那儿有我的email地址。)如果你想提出Python的改进建议或者讨论某种替代设计的价值,用python-ideas邮件列表在python.org。]

我的Python设计目标之一就是所有的Python对象都是“一类公民”。也就是说我希望所有可以在语言中命名的对象(例如整数、字符串、函数、类、模块、方法等)都有同样的地位。这样他们都可以有赋值给变量、放入list、存储在dictionary、传递给参数等操作。

Python的内部实现机制使得这个目标变得简单。所有的Python对象都基于一个共同的C语言数据结构,可在解释器中任意地方调用。变量,list、函数以及其它任一对象都是这个数据结构的变种--这个结构是表示一个如整数这样简单的对象还是像类这样复杂的对象并不重要。

虽然拥有“一切都是一类公民”想法在概念上很简单,仍然有一点和类相关的细微之处值得我再谈谈--也就是让方法成为“一类公民”对象的问题。

考虑如下简单的代码(从上周的博文拷贝而来):


class A:
def __init__(self,x):
self.x = x
def spam(self,y):
print self.x, y


如果方法是“一类公民”对象,那么在Python中它就应该可以像其它对象那样赋给其它变量。例如,一个人可以写“s = A.spam”的代码。这时变量“s”代表了类的一个方法,实际上是一个函数。然而方法和一个普通函数不尽相同。具体来说,方法的第一个参数应当是方法被定义时所在类的实例。

为了解决这个问题,我创建了一个新的类型,它是可调用对象,称为“未绑定方法(unbound method)”。一个未绑定方法实际上是实现方法的函数对象的简单封装,强制第一个参数必须是方法所在类的实例。因此,需要像调用函数那样调用未绑定方法“s”时,就必须传递类A的一个实例作为第一个参数。例如"a = A(); s(a)"[译者注:根据上下文,为能运行,需补全参数,如“a = A(1); s(a, 2)”]。 (*)

一个相关联问题在Python语句是某对象特定实例的方法时会遇到。例如,可以创建一个实例“a=A()”然后再写一句“s = a.spam”。此处变量“s”还是指向类的spam方法,然而这次“s”是通过类的实例“a”得到方法引用的。为了处理这种情况,创建了另一种被称为“绑定方法(bound method)”的可调用对象。这个对象也是表示方法的函数对象的简单封装。但是这次封装隐含的将得到方法时用到的实例存储起来。因此,对随后的“s()”语句,将会在调用方法时把实例“a”作为隐含的第一个参数。

实际上,表示绑定和未绑定方法是同一个内部对象类型。该对象一个某个属性指向对实例的引用。设置为None时,方法是未绑定的。否则就是绑定的。

虽然绑定与否可能看起来没什么重要的,确是类内部工作的一个关键部分。当程序中出现“a.spam()”时,这条语句的执行实际上分为两步。首先,查找“a.spam”发生之处。返回一个绑定方法--这是一个可调用对象。然后,在附加用户提供的参数之后,对这个对象进行函数调用“()”
__________(*)
在Python 3000中,未绑定方法的概念被去除,表达式“A.spam”返回一个简单的函数对象。这是以前的应用经历证明的:由于限制第一个参数必须是A的一个实例对于诊断问题帮助甚微,反而经常成为高级应用的障碍--这种高级应用有人称作“self的鸭子类型(duck type self)”,看起来是个不错的名字。

没有评论: