The History of Python

2010年6月24日星期四

The Inside Story on New-Style Classes

The Inside Story on New-Style Classes

新类内幕

英文原文链接: http://python-history.blogspot.com/2010/06/inside-story-on-new-style-classes.html

原文作者: Guido van Rossum

[提醒,本篇是长文,且多技术细节。]

表面看新类和原来的类实现十分相似,实际上新类还引入了一些新概念:
  • 低级构造方法__new__()
  • descriptors,泛化个性化属性(attribute)存取方式
  • 静态方法与类方法(static methods and class methods)
  • properties (即时计算的attributes)
  • decorators (Python2.4引入)
  • slots
  • 新的MRO(Method Resolution Order, 方法解析顺序)
接下来几个章节我将尝试介绍一下这些概念

低级构造方法和__new__()
往常,类的实例创建后,通过__init__()方法进行初始化。然而有时类的作者希望能控制实例的创建过程-例如,当对象是从存储数据恢复而来时。旧式类(Old-style classes)从未真正提供定制对象创建的机制,虽然有些库模块(例如,"new"模块)提供了某些情况的对象创建非标准方式。

新类引入了新的类方法__new__(),以方便类作者控制类实例创建。类作者通过重载__new__()可以实现一些设计模式,例如(从空闲列表)返回已创建实例的单模式(Singleton Pattern),或者返回其它类(可能是原来类的子类)的实例。__new__还有其它重要用途值得关注,例如,在pickle模块中,在反序列化对象时__new__用于创建实例。这时实例已经生成,而__init__方法尚未调用。

__new__还可用于辅助子类化不可变类型(immutable types)。因为"不可变"这一特性,这类对象无法通过标准的__init__()方法来初始化。于是任何特殊的初始化,需要在对象创建时进行;例如,如果类希望能修改不可变对象包含的值,可在__new__方法对此进行处理,向基类的__new__方法传递修改后的值来实现。

Descriptors

Descriptors 是绑定方法(bound methods)这一概念的泛化,绑定方法对于经典类(classic classes)实现至关重要。在经典类情况,当要查询的实例属性(instance attribute)在实例字典(instance dictionary)中未找到时,就继续在类字典中(class dictionary)查找,然后是递归遍历基类的类字典。当属性是在类字典(class dictionary)中查找到时(而不是在实例字典中)。解释器会检查查找到的对象是否是一个Python函数对象(function object)。如果是,则返回值不是该查找到的查找到的对象,而是将该对象的包装对象(wrapper object)作为一个currying function返回。当包装对象被调用时,它会把实例插入到参数列表最前面,然后调用原来的函数对象。

例如,类C有实例x。假设要做方法调用x.f(0)。这个操作会查找x的属性名"f",然后将0作为参数进行调用。如果"f"是类方法,在查找该属性时会返回一个包装函数(wrapper function),可用如下Python伪代码近似描述:


def bound_f(arg):
return f(x, arg)


当传递参数0来调用包装函数时,它会传递两个参数调用"f":x和0。这是类方法得到"self"参数的基本机制。

另外一种调用函数对象(function object)f(无包装)的方式是通过调用类C的属性名"f"。这时返回的不是包装后的对象而是函数f本身。换句话说,x.f(0)等价于C.f(x, 0)。这是Python中的一种相当基本的等价方式。

对经典类而言,如果查找到的属性是其它类型对象(非函数类型),则不创建包装对象,而是不做变化直接将该类属性返回。这使得类属性可以作为实例变量"缺省"值。例如在上例中,如果类C有一个名为"a"的属性,它的值是1,且x的实例字典中没有"a",那么x.a等于1。对x.a赋值则会在x的实例字典创建名为"a"的键,它的值将会遮蔽(shadow)同名类属性(因为属性查找顺序)。当x.a被删除后,被遮蔽的值(1)又能访问到了。

不幸的是,一些Python开发人员发现这种机制存在缺陷。缺陷之一是这种机制下无法创建"混合"类,也就是部分方法用Python实现,部分方法用C实现的类,因为这种机制下只有Python函数才会被包装,使得调用时能访问实例,而且这一行为是硬编码在语言中。而且也没有明显的方式来定义其它类型函数,例如C++和Java程序员熟悉的静态成员函数。

为了解决这一问题,Python 2.2对上述包装行为的进行了直观的泛化。不再用硬编码方式指定只对Python函数对象进行包装,而对其它对象不做包装。包装相关行为完全留给属性搜索到的对象来处理(上例中是函数f)。如果查找属性时的查找结果是有一个名为__get__特殊方法的对象,则该对象被认为是一个"descriptor"对象,这时__get__方法被调用,由它的返回值都作为属性查找结果。如果该对象没有__get__方法,则不作变化直接返回查找到的对象。要得到原有行为(对函数对象进行包装)而且不需要在实例属性查找代码中对函数对象特殊处理,现在拥有一个__get__方法的函数对象将如同以前一样返回包装对象。而且,用户还可以定义含有名为__get__方法的另外一个类,当这种类的实例在原来的类字典中被属性查找过程找到时,可以按需灵活进行包装。

在对属性查找这一概念进行泛化后,同样思路也有必要推广到属性的赋值与删除操作。因此,对赋值操作x.a=1或者删除操作 del x.a也有相似的处理机制。这时,如果属性"a"是在实例的类字典(而不是实例字典)中,检查类字典的中该对象是否含有名为__set__和__delete__的特殊方法(__del__已另有它用)。这样,通过实现这些函数,一个descriptor对象可以完全控制一个属性的get、set和delete含义。要强调的是,这些定制操作仅是对类字典中descriptor实例而言的,对象实例字典中则无效。

staticmethod, classmethod, and property

Python2.2利用这一新的descriptors机制增加了三个预定义类:类方法(classmethod),静态方法(staticmethod)和property,类方法和静态方法只是简单包装函数对象,通过__get__方法的具体实现,来返回不同种类的封装器(wrappers)。例如,静态方法在函数调用时封装器对参数列表不做任何修改。类方法在函数调用时将封装器将实例的类对象增加为第一个参数,而不是增加实例本身。这两种类型的方法都可以通过实例或者类来调用,且在不同调用者情况下调用时的参数相同。

property类封装器将取值和赋值这一对方法转换为一个"属性"。例如,如果你有如下一个类,


class C(object):
def set_x(self, value):
self.__x = value
def get_x(self):
return self.__x


property封装器可用来产生一个属性"x",当存取x时隐含方式调用了get_x和set_x方法。

一开始引入类方法、静态方法和property这些descriptors时,并没有同时引入相应的新语法。当时看起来同时引入比较重要的新特性以及相应的语法引起的争议过大(新语法的讨论总是引起激烈争论)。因此使用这些特性时,先依照常规定义类和方法,然后用额外语句来"包装"方法,例如:


class C:
def foo(cls, arg):
...
foo = classmethod(foo)
def bar(arg):
...
bar = staticmethod(bar)


对properties也有类似机制:


class C:
def set_x(self, value):
self.__x = value
def get_x(self):
return self.__x
x = property(get_x, set_x)


Decorators

这种方式有一个不利之处,用户阅读类的代码时,只有读完一个方法的声明,到了结尾部分才知道该方法是不是类方法或者静态方法(或其它用户自定义形式)。终于,Python2.4引入了新的语法,允许以如下方式撰写代码:


class C:
@classmethod
def foo(cls, arg):
...
@staticmethod
def bar(arg):
...


在函数声明前一行构造的@表达式(@expression)称为decorator。(不要和前面的descriptor混淆,实现__get__包装时所讨论到的才是descriptor。)选择这个decorator语法(decorator syntax)(从Java的annotations借鉴而来。)引起了无休止的争论,最终由BDFL宣告(BDFL pronouncement)来确定。(David Beazley写了一篇关于术语BDFL的历史典故,我会另外单独写一篇贴出。)

decorator特性已成为最成功的Python语言特性之一,各种decorator应用超过我当初最乐观的期待。特别是在Web框架中大量应用。鉴于如此成功,在Python2.6中,decorator语法从函数定义进一步扩展到类定义。

Slots

descriptor的引入带来了另外一个新特性,也即类的__slots__属性。例如一个类,定义如下:


class C:
__slots__ = ['x','y']


这儿的__slots__有如下含义。首先,对象的有效属性名被限制为__slots__值的列表。其次,由于属性名范围已经确定,__dict__属性因不再需要而被移除(除非某个基类已经包含了__dict__属性,子类化当前类且不包含__slots__属性可以再次拥有__dict__属性)。这样各属性存储在预先确定顺序的array中。因此,每一个slot属性实际是一个descriptor对象,利用该属性在array中的位置(index)来调用其set/get方法。在具体实现方面,该特性完全由C语言实现,性能优异。

一些人错误的认为__slots__的目的在于(通过限制属性名)增加代码的安全性。实际上,我的终极目标聚焦在性能方面。__slots__是一个有趣的descriptor应用,我担心对类的改动总是带来性能方面的不利影响。以descriptor而言,为了使之正常工作,对象属性的任何操作都需要首先检查类字典来判定该属性是否实际上是一个数据descriptor(data descriptor)。如果确实是,则由该descriptor负责属性存取,而不是通常情况下直接操作属性字典。这个额外的检查也意味着检查每个实例的字典之前要有一个额外的查找操作。因此,__slots__可以作为一种优化数据属性的方法,作为当有人对新类的性能不满意时的补救。结果证明这种性能的担心并不必要,但是撤除__slots__也为时已晚了。而且,正确使用slots确实能增加性能,尤其是创建大量小对象时有利于降低内存占用。

我把Python MRO(Method Resolution Order方法解析顺序)的介绍留在下一篇。

2010年6月22日星期二

New-style Classes

新类

英文原文链接: http://python-history.blogspot.com/2010/06/new-style-classes.html

原文作者: Guido van Rossum

[漫长的间歇,本系列blog回归了!我将接着去年的写,我尽量加快速度。]

前面我提到Python中对类(Class)的支持是后来添加的。具体实现符合典型的Python式“抄近路”哲学(cut corners),然而随着Python的发展,类(Class)的实现暴露出各种问题,也成为了Python高级用户的热门话题。

类(Class)实现问题之一是限制了对内置类型(built-in types)的子类化(subclass),例如lists, dictionaries, strings和其它一些对象(object)成为无法子类化的“特殊”类型。对于一门“面向对象(object oriented)”语言来说,这种限制多少有些尴尬。

另一问题是整个类型系统(type system)对于用户自定义类(user defined classes)来说是“错”的。假如你创建两个对象a和b,即使a和b是毫无关联的两个类的实例,语句type(a) == type(b)的值仍为真。无须讳言,对于熟悉C++或者Java语言的程序员来说,由于C++和Java等语言中类(Class)与底层的类型系统(type system)紧密相连,该现象相当诡异。

在Python的2.2版本,我重写了类实现,而且是“正确的实现”。这次重写一个Python主要子系统是目前为止最为野心勃勃的变动,肯定会有人因此说我得了“第二系统综合症(second-system syndrome)”。这次重写,不仅解决了内置类型的子类化这一直接问题,我还增加了对真正的元类(metaclass)的支持,并尝试改善原来多重继承(multiple inheritance)时过于简陋的方法解析顺序(method resolution order)。这项工作的主要受Ira Forman和Scott Danforth撰写的《Putting Metaclasses to Work》影响,书中阐明了元类的概念,及其与Smalltalk中类似概念的区别。

这次重写类实现的一个特点是,新类(new-style class)是作为语言的一个新特性引入,而不是完全替换掉旧的类。实际上为了保持向后兼容,Python 2中缺省生成的类仍然是旧的类。要创建一个新类,你只需子类化一个现存的新类,例如object(object是新类类层次的根)。例如:


class A(object):
statements
...


这次新类的引入取得的成绩斐然。新的元类得到了框架实现者的积极支持,实际上由于例外情况变少也使得讲解类更为容易。向后兼容使得新类进化过程中旧类依然正常工作。最后,虽然旧类终将从语言中移除,用户已习惯了用"class MyClass(object)"来声明一个类,也不错。
--
cut corners可参考前期博文:英文或中文译文

second-system syndrome可参考:http://en.wikipedia.org/wiki/Second-system_effect

2009年5月1日星期五

元类与扩展类(传说中“杀手的笑话”)

英文原文链接:http://python-history.blogspot.com/2009/04/metaclasses-and-extension-classes-aka.html

原文作者:Guido van Rossum

在Python本来的实现中,类是一类公民对象,可以像处理其它对象一样来操作类。然而创建一个类的过程却固定不变。具体来说,当你定义如下一个类时:


class ClassName(BaseClass, ...):
...method definitions.



类的内容将会运行在它自身的局部字典。类的名字,基类组成的tuple,和这个局部字典将会传递给内部负责生成类对象的类创建函数。类对象生成的动作隐藏在幕后,一般用户对实现细节不需关心。

Don Beaudry是第一个指出如此设计让专业用户失去深层次控制机会的人。具体来说,既然类只是诸多对象的一种,为何不能创建新种类的类来定制其行为呢?他还提出了一个方案,只需对解释器稍加修改就可以利用C代码生成允许新类型的类对象了。关于相关修改的介绍最初出现在1995年,长期被称作“Don Beaudry hook”或者“Don Beaudry hack”,命名不明确是故意开玩笑的结果了。Jim Fulton随后对修改进行了通用性改善,这个修改一直保留在语言中(尽管文档中未说明)直到Python2.2版本为止,那时通过new-style class的引入对元类有了真正的支持(详情见下文)。

Don Beaudry hook的基本想法是,如果在类生成的最后阶段能支持由用户提供的函数,那么专业用户就可以创建定制的类对象。具体来说,如果类名、基类、局部字典可以传递给不同的构建函数,那么该函数在创建类对象时就可以随心所欲,自由发挥了。我唯一的顾虑是不希望由此对类语法做过多更动,毕竟已很稳固。

为了实现目的,这个hook要求用C语言创建一个可被调用(callable)的新类型对象。然后,当这样一个可调用类型的实例在类语句中作为基类时,类创建代码会神秘的调用相应类型对象,而不是标准的类对象。创建类(及其实例)的行为完全依赖于扩展模块提供的可调用对象。

现代Python用户可能会觉得奇怪,感觉有些绕弯。但在当时,类型对象是不可调用的--例如‘int’不是一个内置类型而是一个内置函数,该函数可以返回int对象的实例,而int类型本身即不易存取也难以调用。用户自定义类当然是可以调用的,不过这是因为最初设计类时就将其当做调用指令的特殊情况。Don Beaudry最终说服我接受这个想法,随后又导致了元类和new-style类,并最终导致经典类的消亡。

一开始,只有Don Beaudry自己的Python扩展MESS是唯一利用这一特性的。然而到了1996年,Jim Fulton开发了一个十分流行的第三方模块,称为Extension Classes,也利用了Don Beaudry hook。Extension Classes包在Python2.2引入元类作为对象机制的标准部分后最终失去意义。

在Python1.5中,我去掉了需要写C扩展才能使用Don Beaudry hook的限制。另外为了检查基类类型是否可调用,类生成代码会检查基类中一个名为“__class__”的属性,若存在就调用它。我写了一篇短文介绍这一特性,这是许多Python用户第一次听闻元类的说法。由于这个想法令人头大,很快这篇短文就被冠以昵称“杀手的笑话(The Killer Joke,一个Monty Python里的典故)”。
Don Beaudry hook贡献影响最长的部分应当是类创建函数的API,在Python2.2中新的元类机制中得以保存。如前所述,类创建函数调用时需要三个参数:一个表示类名的string、一个给定基类的tuple(包括基类为空或者只有单个基类情况)和一个内容为方法定义缩进代码块(不仅限于方法,也可以是其它类级别的语句)的字典。类创建函数的返回值是一个类名为名称的变量。

最初,创建类只有简单的内部API。The Don Beaudry hook运用了同样的调用机制,由此成为公开API。这个API重要一点是包含方法定义的块在类创建函数被调用前就执行了。这一点限制了元类的效用,因此元类不能改变方法定义运行时命名空间的初始内容。

Python 3000中对此做了改动,现在一个元类在类体运行时可以提供一个替代映射对象。为支持这一点,明确指定元类的语法也做了变动:在基类中使用关键词参数就是为了这个目的而引入的。

下一篇我会接着写元类如何导致2.2中new-style class的出现(以及在3.0中经典类的消失)。

新闻!有日语翻译版本啦!

英文原文链接:http://python-history.blogspot.com/2009/04/new-now-in-japanese.html

原文作者:Guido van Rossum

现在有一个日语翻译的blog了。耶!还有一个西班牙版本,还有一个法语版本(抱歉,我还不知道具体链接了--知道的请告诉我。)

我对这些语言不了解(或者不熟)因此本文并不代表我“认可”这些翻译,但是我很高兴有这些翻译版本的存在。如果你想开始另外一种语言的翻译,请自便--也请给我一个链接以便我可以更新这个页面。如果你看到翻译里面的不妥之处,请直接和译者联系。(但是,我不希望只是复制我的博文。这样做没有意义,而且一般拷贝热门博文的典型网站都是毫无创新劳动目的只图吸引眼球,以便推销他们的广告。)

大蛇在行动

英文原文链接:http://python-history.blogspot.com/2009/04/and-snake-attacks.html

原文作者:Greg Stein

好,当1995年我第一次接触Python语言的时候,任何把Python和“蛇”联系起来的做法都是禁止的。Python得名于Monty Python,而不是爬行动物。如果有人敢说不,那一定是Knights who say Ni,或者the Rabbit of Caerbannog。

再往前,回到1994年,当时我正在LPMUD里面玩的乐不思蜀。那时Web还很罕见,宽带更没听说过。大家在玩这些低带宽的娱乐。

等一下,再往前回一步,到1979年。那时我有了第一台电脑苹果II,Colossal Cave是我最喜欢的游戏之一。之后不久,我学习和玩Zork。我深深迷恋通过计算机和虚拟故事交互并在故事中流连忘返。游戏勾住了我,让我在计算机里面有了另一个生命。(于是,你可以想象时隔四分之一世纪多之后当我遇到Don Woods时是如何的欣喜若狂!)

MUD里面的场景深深打动了我。但是我更想从事建立游戏。我遇到了John Viega,他是一个LPMUD游戏的资深作者、程序员和设计师。当时他在维吉尼亚大学计算机图形实验室工作,参与一个称作Alice的系统。系统设计目的希望降低对计算机知识的要求,需要容易学习以方便用户创见动画人物形象。他们选择了Python语言,这是因为Python的清晰强大易用。

John是Python的极力拥护者,对我说“你一定要学习Python!”“好的,好的。”,我说。这门语言易学易用又功能强大。Python做任何事情都方便。

这是1995年二月的事情了,从那以后我在学习使用Python的路上再没有回头。

当时我根本不知道Python对我的职业和生活有如此关键的作用。感谢Guido,发明了Python。

Python函数编程特性的起源

英文原文链接:http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html


原文作者:Guido van Rossum


不管别人怎么说的想的,我是从来也没想到Python会被函数式编程语言影响如此之深。我对C和Algol 68这类命令式编程语言更为熟悉,而且即使我设定函数为一类公民对象,我也没有将Python看做是函数式编程语言。然而从一开始,就清楚的发现用户希望利用list和函数做更多的事情。


一个对list的常见操作是对其每一个元素调用某个函数并将结果生成一个新的list,例如:



def square(x):
return x*x

vals = [1, 2, 3, 4]
newvals = []
for v in vals:
newvals.append(square(v))

Lisp和Scheme等函数式编程语言会提供内置函数来实现类似操作。因此熟悉这些语言的早期用户会自己动手在Python中实现类似功能的函数,例如:



def map(f, s):
result = []
for x in s:
result.append(f(x))
return result

def square(x):
return x*x

vals = [1, 2, 3, 4]
newvals = map(square,vals)

上述代码的一点小问题是许多人不喜欢应用到列元素的操作被分开定义为一个的单独函数。Lisp等语言允许在进行映射函数调用时直接“当场”定义函数。例如,在Scheme语言中你可以利用lambda在一条语句中创建匿名函数并执行映射操作,像下面这样:



(map (lambda (x) (* x x)) '(1 2 3 4))

虽然Python中函数是一类公民对象,当时并没有任何创建类似匿名函数的机制。


到1993年底,用户已经提出了各种创建匿名函数的思路,和各种list操纵函数如map()、filter()和reduce()。例如,Mark Lutz(《Programming Python》的作者)贴过一段利用exec来产生函数的函数代码:



def genfunc(args, expr):
exec('def f(' + args + '): return ' + expr)
return eval('f')

# Sample usage
vals = [1, 2, 3, 4]
newvals = map(genfunc('x', 'x*x'), vals)

Tim Peters又跟进提出了一个语法上略微简化的方案,允许用户这样写代码:



vals = [1, 2, 3, 4]
newvals = map(func('x: x*x'), vals)


这些都表明了对这一功能确有实际需求。然而当时利用exec手工将包含代码的字符串转换为匿名函数过于“儿戏”。于是1994年1月,map()、filter()和reduce()被添加到标准库。另外,lambda操作符也被语法上直观的引入以创建匿名函数(或称匿名表达式)。例如:



vals = [1, 2, 3, 4]
newvals = map(lambda x:x*x, vals)

这些是早期的重要贡献代码。不幸的是我想不起作者是谁了,而SVN的日志也没有记录。如果是你的话,请留言!


我从来也没有喜欢过“lambda”这个术语,但是又找不到更合适的替代者,于是也只好用了这个。毕竟lambda是当时无名代码贡献者的选择,那时大的改动比现在需要的讨论要少的多,讨论少的影响有好也有坏。


Lambda最初只用来当做定义匿名函数的语法工具。然而选择lambda这个词带来了许多意外后果。例如熟悉函数式编程语言的用户希望Python中的lambda能和其它语言相似。结果他们发现Python的实现缺少对高级特性的支持。例如一个小问题是lambda表达式无法引用上下文中变量。于是下面代码中map()函数将会出错,提示lambda函数中引用了未定义的变量“a”。



def spam(s):
a = 4
r = map(lambda x: a*x, s)

可以用下面代码来凑活着解决这个问题,不过涉及设置缺省参数和传递隐含参数很不直观:



def spam(s):
a = 4
r = map(lambda x, a=a: a*x, s)

这个问题的“标准”答案是对所有被函数引用的相关上下文中局部变量,内部函数隐含支持对这些变量的引用。这也就是“闭包(closure)”,函数式编程语言中的一个关键特性。然后Python直到2.2版本才引入对闭包的支持(python2.1版本也可以通过“from the future”方式使用)。


有意思的是,map、filter和recude函数,他们是最初引入lambda和其它函数式编程语言特征的源动力,自身却很大程度上被list comprehensions和generator expressions盖过了风头。以至于在Python3.0中reduce函数已经从内置函数列表中移除。(不必担心lambda、map和filer,他们已经被保留了:-)


要提到的是,即使我还是没有把Python当做函数式编程语言的情况下,闭包的引入却成功的用于开发其它高级语言特性。例如new-style class的某些方面,decorator和依赖它的其它现代特性。


最后,即使一部分函数式编程语言特征已引入多年,Python仍然缺乏一些“真正”函数式编程语言的特性。例如Python不支持特定情况的优化(例如尾递归)。总起来说,因为Python的深度动态本质,无法做到Haskell或者ML这类函数式编程语言的编译时优化。这是好事。

大改名

英文原文链接:http://python-history.blogspot.com/2009/03/great-or-grand-renaming.html


原文作者:Guido van Rossum

Python初创时期,我总是将其作为一个独立程序来用,偶尔连接一些第三方库进来。因此源代码随意定义全局名称(当然要符合C/连接器要求)例如“object”、“getlistitem”和“INCREF”诸如此类。随着Python流行开来,人们开始要求“嵌入”版本的Python,以便Python自身作为一个库供其它应用程序连接-这和Emacs与Lisp解释器之间混合的方式还不一样。


糟糕的是,嵌入由于命名冲突而复杂化,Python全局名和那些嵌入Python的应用之间会发生名称冲突-特别是“object”很常见。为了解决这个问题而进行了命名约定,约定所有的Python全局名称开头为“Py”或者“_Py”(用于因技术原因不得不作为全局变量的内部名称)或者“PY”(用于宏)。


为了向下兼容(已经有许多第三方扩展模块)和方便核心开发人员转换(在他们的大脑中原来的旧名字已经根深蒂固)分成了两个阶段。第一个阶段连接器看到的是旧名字,而源代码使用的是新名称,用一个包含许多规则的C语言预编译宏来完成转换。第二个阶段连接器看到的是新名字,但是为了继续支持还没移植的原有扩展模块,另外一套宏用于旧名字转换为新名字。在任一阶段,代码都可以在新旧名字混合情况下正常工作。


我研究了一下Subversion日志中名称改变的历史。我发现1995年1月12日的r4583在所有头文件中引入新名称,标志伟大改名(great renaming)第二阶段的开始。然而直到1996年12月C源代码中的改名工作仍在继续进行中。也是大约在这个时候,重命名本身也被重新称呼,checkin comments常称之为“宏大命名(Grand Renaming)”了。在2000年5月,作为Python1.6版本发布的成果之一,向下兼容的宏被移除。check-in comment r15313庆祝了此一事件。


此事归功于Barry Warsaw和Roger Masse,他们做了大量细致的工作,对源文件内容逐个进行重新命名(有些脚本来帮助这一过程)。对大量标准库进行unit test也有助于他们重命名,这是又一个繁琐的工作。


Wikipedia上对Great Renaming有一个早期条目,是指USENET groups的改名。我称Python的改名为Great Renaming时可能正模糊记得这一事情。我还看到有些参考资料指向随后Sphinx的Grand Renaming,这是用于生成Python文档的包。看起来Zope也有一个Grand Renaming,最近Py3k也讨论到了术语PyString到PyBytes的重命名(当然这个和前面那些比要小得多)。


伟大改名或者宏大改名(Great or Grand Renamings)对软件开发社区来说是一个长期的创伤,因为这需要程序员的脑袋接受再教育、重写文档、对改名前提议的补丁集成到到改名后的分支变得困难。(尤其是还存着未改名分支时更是如此。)