What’s New in Python 2.0

作者:上午。 Kuchling和Moshe Zadka

Introduction

Python的新版本2.0版本于2000年10月16日发布。本文涵盖了2.0中令人兴奋的新功能,突出了一些其他有用的更改,并指出了一些不兼容的更改可能需要重写代码。

Python的开发从来不会完全停止发布,并且始终提交稳定的错误修复和改进。一些小的修复,几个优化,额外的文档字符串和更好的错误消息进入2.0;列出他们所有将是不可能的,但他们肯定是重要的。如果要查看完整列表,请参阅公开可用的CVS日志。这个进步是由于为PythonLabs工作的五个开发人员现在得到报酬花他们的日子修复错误,还由于改进的沟通,由于移动到SourceForge。

What About Python 1.6?

Python 1.6可以被认为是契约义务Python版本。核心开发团队在2000年5月离开CNRI后,CNRI要求创建一个1.6版本,其中包含了在CNRI上执行的所有Python工作。因此,Python 1.6表示截至2000年5月的CVS树状态,最重要的新功能是Unicode支持。发展在5月后继续,当然,所以1.6树收到一些修复,以确保它向前兼容Python 2.0。1.6因此是Python的演化的一部分,而不是一个分支。

所以,你应该对Python 1.6感兴趣吗?可能不会。1.6final和2.0beta1版本是在同一天(2000年9月5日),计划在一个月左右内完成Python 2.0。如果你有应用程序维护,似乎没有什么意义,打破事情,移动到1.6,修复它们,然后在一个月内通过移动到2.0的另一轮破裂;你最好直接去2.0。本文档中描述的大多数真正有趣的功能仅在2.0中,因为很多工作是在5月和9月之间完成的。

New Development Process

Python 2.0中最重要的变化可能不是代码,而是Python的开发:2000年5月,Python开发人员开始使用SourceForge提供的用于存储源代码,跟踪错误报告和管理队列的工具的补丁提交。要报告Python 2.0的错误或提交补丁,请使用位于https://sourceforge.net/projects/python/的Python项目页面中提供的错误跟踪和补丁管理器工具。

现在在SourceForge托管的最重要的服务是Python CVS树,版本控制的存储库包含Python的源代码。以前,大约有7个左右的人对CVS树有写访问权限,所有补丁都必须由这个简短列表中的一个人进行检查和检查。显然,这不是很可扩展。通过将CVS树移动到SourceForge,可以向更多的人授予写访问权限;截至2000年9月,有27人能够检查变化,增加了四倍。这使得大规模变更成为可能,如果他们必须通过一小组核心开发人员进行过滤,那么就不会尝试。例如,有一天Peter Schneider-Kamp把它放到了他的头脑中,以删除K&R C兼容性,并将Python的C源代码转换为ANSI C.在获得python-dev邮件列表的批准后,他发布了一系列的checkins,大约一个星期,其他开发人员加入了帮助,这项工作完成了。如果只有5个人有写访问权限,那么这个任务可能被视为“不错,但不值得花费时间和精力”,而且它永远不会完成。

转向使用SourceForge的服务导致了发展速度的显着提高。修补程序现在获得提交,评论,由原始提交者以外的人修改,并在人与人之间来回弹动,直到补丁被认为值得检查。在一个中心位置跟踪错误,可以将其分配给特定的人进行修复,我们可以计算打开的错误的数量来衡量进度。这不是没有成本:开发人员现在有更多的电子邮件来处理,更多的邮件列表跟随,并且专门的工具必须为新的环境写。例如,SourceForge发送完全无用的默认修补程序和错误通知电子邮件,因此Ka-Ping Yee写了一个HTML屏幕抓取工具,可以发送更多有用的消息。

添加代码的容易导致了一些初始成长的痛苦,例如代码在它准备好之前检查或没有得到开发人员组的明确同意。出现的审批过程有点类似于Apache组使用的审批过程。开发人员可以在补丁上投+1,+ 0,-0或-1; +1和-1表示接受或拒绝,而+0和-0表示开发者对变化大多无关,尽管具有轻微的正或负倾斜。与Apache模型最大的变化是投票本质上是咨询,让Guido van Rossum,谁拥有仁义的生命的独裁者的地位,知道一般的意见是什么。他仍然可以忽视投票的结果,批准或拒绝改变,即使社区不同意他。

生成实际的补丁是添加新功能的最后一步,并且通常比较早的任务提出一个好的设计。新功能的讨论常常会爆炸成冗长的邮件列表线程,使讨论很难跟进,没有人能够阅读每个发布到python-dev。因此,已经建立了一个相对正式的过程来编写Python增强提议(PEP),并在Internet RFC过程上建模。PEP是描述拟议的新特征的文件草案,并持续修订,直到社群达成共识,接受或拒绝该建议。引自PEP 1,“PEP目的和指南”:

PEP stands for Python Enhancement Proposal. A PEP is a design document providing information to the Python community, or describing a new feature for Python. The PEP should provide a concise technical specification of the feature and a rationale for the feature.

We intend PEPs to be the primary mechanisms for proposing new features, for collecting community input on an issue, and for documenting the design decisions that have gone into Python. The PEP author is responsible for building consensus within the community and documenting dissenting opinions.

阅读PEP 1的其余部分,了解PEP编辑过程,样式和格式的详细信息。PEP保存在SourceForge上的Python CVS树中,尽管它们不是Python 2.0发行版的一部分,并且还可以从https://www.python.org/dev/peps/下载。 t0>。截至2000年9月,有25个PEPS,从PEP 201,“锁步迭代”到PEP 225,“单元/对象运算符”。

Unicode

Python 2.0中最大的新功能是新的基本数据类型:Unicode字符串。Unicode使用16位数字来表示字符,而不是ASCII使用的8位数字,这意味着可以支持65,536个不同的字符。

Unicode支持的最终接口是通过python-dev邮件列表上无数狂热的讨论达成的,主要由Marc-AndréLemburg基于Fredrik Lundh的Unicode字符串类型实现来实现。接口的详细解释写为 PEP 100,“Python Unicode集成”。本文将简单介绍有关Unicode接口的最重要的要点。

在Python源代码中,Unicode字符串写为u"string"任意Unicode字符可以使用新的转义序列\uHHHH写入,其中HHHH是从0000到FFFF的4位十六进制数字。也可以使用现有的\xHHHH转义序列,八进制转义可用于高达U + 01FF的字符,由\777表示。

Unicode字符串,就像普通字符串一样,是一个不可变序列类型。它们可以索引和切片,但不能在适当位置修改。Unicode字符串具有以所需编码方式返回8位字符串的编码( [编码] 编码以字符串命名,例如'ascii''utf-8''iso-8859-1'编解码器API被定义用于实现和注册新的编码,然后可用于整个Python程序。If an encoding isn’t specified, the default encoding is usually 7-bit ASCII, though it can be changed for your Python installation by calling the sys.setdefaultencoding(encoding) function in a customised version of site.py.

组合8位和Unicode字符串总是强制使用Unicode,使用默认的ASCII编码; 'a' + u'bc'的结果是u'abc'

新内建函数已添加,现有内置修改为支持Unicode:

  • unichr(ch)返回一个长度为1个字符的Unicode字符串,其中包含字符ch
  • ord(u),其中u是一个1个字符的正则或Unicode字符串,返回字符的数字作为整数。
  • unicode(string [, encoding] [, errors] ) creates a Unicode string from an 8-bit string. encoding是一个字符串,用于命名要使用的编码。errors参数指定对当前编码无效的字符的处理;通过'strict',因为值会导致任何编码错误产生异常,而'ignore'会导致错误被忽略,并且'replace'
  • exec语句和各种内置函数,如eval()getattr()setattr()(这可能是修复这个过程错过了一些内置函数;如果你发现一个内建函数接受字符串,但不接受Unicode字符串,请报告为一个错误。)

新模块unicodedata提供了Unicode字符属性的接口。例如,unicodedata.category(u'A')返回2个字符的字符串'Lu','L'表示它是一个字母,'u'表示它是大写的。unicodedata.bidirectional(u'\u0660')返回“AN”,表示U + 0660是阿拉伯数字。

codecs模块包含查找现有编码和注册新编码的功能。除非你想实现一个新的编码,你最常使用codecs.lookup(encoding)函数,它返回一个4元素元组:(encode_func, t3> decode_func, stream_reader, stream_writer)

  • encode_func是一个接受Unicode字符串并返回一个2元组(string, length)的函数。string是包含转换为给定编码的Unicode字符串的一部分(可能是全部)的8位字符串,length告诉您有多少Unicode字符串被转换。
  • decode_funcencode_func相反,采用8位字符串并返回2元组(ustring, ,由结果的Unicode字符串ustring和整数length组成,告诉消耗8位字符串的多少。
  • stream_reader是一个支持从流中解码输入的类。stream_reader(file_obj)返回支持read()readline()readlines()这些方法都将从给定的编码转换并返回Unicode字符串。
  • stream_writer,类似地,是支持编码输出到流的类。stream_writer(file_obj)返回支持write()writelines()方法的对象。这些方法期望Unicode字符串,将它们转换为给定的输出编码。

例如,以下代码将Unicode字符串写入文件,并将其编码为UTF-8:

import codecs

unistr = u'\u0660\u2000ab ...'

(UTF8_encode, UTF8_decode,
 UTF8_streamreader, UTF8_streamwriter) = codecs.lookup('UTF-8')

output = UTF8_streamwriter( open( '/tmp/output', 'wb') )
output.write( unistr )
output.close()

然后,以下代码从文件中读取UTF-8输入:

input = UTF8_streamreader( open( '/tmp/output', 'rb') )
print repr(input.read())
input.close()

通过re模块可以获得Unicode感知的正则表达式,该模块具有名为SRE的新底层实现,由Secret Labs AB的Fredrik Lundh编写。

添加了一个-U命令行选项,它使Python编译器将所有字符串字面值解释为Unicode字符串字面值。这旨在用于测试和未来保护您的Python代码,因为一些未来版本的Python可能会丢弃对8位字符串的支持并且只提供Unicode字符串。

List Comprehensions

列表是Python中的主力数据类型,许多程序在某个时候操作列表。对列表的两个常见操作是循环遍历它们,并且挑选出满足特定标准的元素,或者对每个元素应用一些函数。例如,给定一个字符串列表,您可能需要拉出包含给定子字符串的所有字符串,或者从每行中删除尾部空格。

现有的map()filter()函数可用于此目的,但它们需要一个函数作为其参数之一。这是很好的,如果有一个现有的内建函数,可以直接传递,但如果没有,你必须创建一个小功能来做所需的工作,Python的范围规则使结果丑陋,如果小功能需要额外信息。以上一段中的第一个例子,找到列表中包含给定子字符串的所有字符串。你可以这样写:

# Given the list L, make a list of all strings
# containing the substring S.
sublist = filter( lambda s, substring=S:
                     string.find(s, substring) != -1,
                  L)

由于Python的范围规则,使用默认参数,以便由lambda语句创建的匿名函数知道正在搜索的子字符串。列表推导式make this cleaner:

sublist = [ s for s in L if string.find(s, S) != -1 ]

列表推导式有以下形式:

[ expression for expr in sequence1
             for expr2 in sequence2 ...
             for exprN in sequenceN
             if condition ]

t>子句中的for ... in序列不必具有相同的长度,因为它们不是并行迭代,而是从左到右;这将在以下段落中更清楚地解释。生成的列表的元素将是表达式的连续值。最后的if子句是可选的;如果存在,表达式只有计算,并且如果条件为真,则将其添加到结果中。

为了使语义非常清楚,列表推导式等价于以下Python代码:

for expr1 in sequence1:
    for expr2 in sequence2:
    ...
        for exprN in sequenceN:
             if (condition):
                  # Append the value of
                  # the expression to the
                  # resulting list.

这意味着,当 t>子句中有for ... in如果你有两个长度为3的列表,输出列表是9个元素长:

seq1 = 'abc'
seq2 = (1,2,3)
>>> [ (x,y) for x in seq1 for y in seq2]
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
('c', 2), ('c', 3)]

为了避免在Python语法中引入歧义,如果表达式正在创建元组,它必须用圆括号括起来。下面的第一个列表推导式是一个语法错误,而第二个是正确的:

# Syntax error
[ x,y for x in seq1 for y in seq2]
# Correct
[ (x,y) for x in seq1 for y in seq2]

列表推导式的想法最初来自函数式编程语言Haskell(https://www.haskell.org)。Greg Ewing最有效地论证了将它们添加到Python中,并编写了初始列表推导式补丁,然后在python-dev邮件列表上讨论了一个看似无休止的时间,并通过Skip Montanaro进行了更新。

Augmented Assignment

增强的赋值运算符,另一个长期被请求的特性,已被添加到Python 2.0。扩充赋值运算符包括+=-=*=等。For example, the statement a += 2 increments the value of the variable a by 2, equivalent to the slightly lengthier a = a + 2.

The full list of supported assignment operators is +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>=, and <<=. Python类可以通过定义名为__iadd__()__isub__()等的方法覆盖扩充的赋值运算符。例如,以下Number类存储一个数字,并且支持使用+ =创建具有递增值的新实例。

class Number:
    def __init__(self, value):
        self.value = value
    def __iadd__(self, increment):
        return Number( self.value + increment)

n = Number(5)
n += 3
print n.value

使用增量的值调用__iadd__()特殊方法,并应返回具有适当修改值的新实例;此返回值被绑定为左侧变量的新值。

扩充赋值运算符首先在C编程语言中引入,并且大多数C派生语言,例如awk,C ++,Java,Perl和PHP也支持它们。增强的分配补丁由Thomas Wouters实现。

String Methods

到目前为止,字符串操作功能在string模块中,这通常是用C编写的strop模块的前端。添加Unicode对于strop模块,因为这些函数都需要重写以接受8位或Unicode字符串。对于诸如string.replace()之类的函数,它需要3个字符串参数,这意味着8个可能的排列和相应的复杂代码。

相反,Python 2.0将问题推送到字符串类型,使通过8位字符串和Unicode字符串的方法的字符串操作功能可用。

>>> 'andrew'.capitalize()
'Andrew'
>>> 'hostname'.replace('os', 'linux')
'hlinuxtname'
>>> 'moshe'.find('sh')
2

有一件事情没有改变,一个值得注意的愚人节的笑话尽管如此,Python字符串是不可变的。因此,字符串方法返回新字符串,并且不修改它们操作的字符串。

旧的string模块仍然是为了向后兼容性,但它大多是作为新字符串方法的前端。

在2.0之前的版本中没有并行的两个方法,尽管它们在JPython中存在一段时间,但是startswith()endswith()s.startswith(t) is equivalent to s[:len(t)] == t, while s.endswith(t) is equivalent to s[-len(t):] == t.

另一个值得特别提及的方法是join()The join() method of a string receives one parameter, a sequence of strings, and is equivalent to the string.join() function from the old string module, with the arguments reversed. 换句话说,s.join(seq)等同于旧的string.join(seq, s) t2 >。

Garbage Collection of Cycles

Python的C实现使用引用计数来实现垃圾容器。每个Python对象都维护着指向自身的引用数量的计数,并在创建或销毁引用时调整计数。一旦引用计数达到零,对象不再可访问,因为您需要对对象的引用以访问它,并且如果计数为零,则不再存在引用。

引用计数有一些令人愉快的属性:它很容易理解和实现,并且最终的实现是可移植的,相当快速,并与其他实现自己的内存处理方案的库反应良好。引用计数的主要问题是它有时不会意识到对象不再可访问,导致内存泄漏。当有引用循环时,会发生这种情况。

考虑最简单的可能循环,一个类实例,它有自己的引用:

instance = SomeClass()
instance.myself = instance

在执行上述两行代码之后,instance的引用计数为2;一个引用来自变量'instance',另一个来自实例的myself属性。

如果下一行代码是del 实例,会发生什么?instance的引用计数减1,因此引用计数为1; myself属性中的引用仍然存在。然而实例不再能通过Python代码访问,它可以删除。如果多个对象具有对彼此的引用,则这些对象可以参与循环,从而导致所有对象泄漏。

Python 2.0通过定期执行循环检测算法来修复这个问题,循环检测算法寻找不可访问的循环并删除所涉及的对象。新的gc模块提供了执行垃圾容器,获取调试统计信息和调整收集器参数的功能。

运行循环检测算法需要一些时间,因此会导致一些额外的开销。希望在我们从循环容器中获得使用2.0的经验之后,Python 2.1将能够通过仔细调整来最小化开销。还不清楚性能损失多少,因为基准测试是棘手的,并且取决于程序创建和销毁对象的频率。通过指定--without-cycle-gc开关,可以在编译Python时禁用循环检测。如果您无法承受微小的速度损失或怀疑循环容器有错误当运行configure脚本时。

几个人解决这个问题,并贡献了一个解决方案。循环检测方法的早期实现由Toby Kelsey撰​​写。当前的算法是由Eric Tiedemann在访问CNRI期间提出的,Guido van Rossum和Neil Schemenauer写了两个不同的实现,后来被Neil集成。许多其他人提供了建议; python-dev邮件列表的2000年3月的档案包含了大部分相关讨论,特别是在标题为“Reference cycle容器for Python”和“Finalize again”的线程中。

Other Core Changes

对Python的语法和内建函数进行了各种小的更改。没有一个变化是深远的,但他们是方便的方便。

Minor Language Changes

新的语法使得用参数的元组和/或关键字参数的字典来调用给定的函数更加方便。In Python 1.5 and earlier, you’d use the apply() built-in function: apply(f, args, kw) calls the function f() with the argument tuple args and the keyword arguments in the dictionary kw. apply()在2.0中是相同的,但是由于Greg Ewing的补丁,f(* args, ** kw) t4 >是一种更短更清晰的方式来实现相同的效果。此语法与用于定义函数的语法是对称的:

def f(*args, **kw):
    # args is a tuple of positional args,
    # kw is a dictionary of keyword args
    ...

The print statement can now have its output directed to a file-like object by following the print with >> file, similar to the redirection operator in Unix shells. 以前,您必须使用类似文件的对象的write()方法,它缺少print的方便和简单,或者可以分配一个新值到sys.stdout,然后恢复旧值。为了将输出发送到标准错误,更容易写这个:

print >> sys.stderr, "Warning: action field not supplied"

Modules can now be renamed on importing them, using the syntax import module as name or from module import name as othername. 补丁由Thomas Wouters提交。

使用%操作符时,可以使用新的格式样式; '%r'将插入其参数的repr()这也是从对称考虑添加的,这次是为了与现有的'%s'格式样式对称,它插入其参数的str()例如,'%r %s' ('abc', abc')返回包含'abc' abc的字符串。

以前没有办法实现一个在操作符号中覆盖Python内建inobj in seq returns true if obj is present in the sequence seq; Python computes this by simply trying every index of the sequence until either obj is found or an IndexError is encountered. Moshe Zadka贡献了一个补丁,其中添加了__contains__()魔法,用于为in提供自定义实现。另外,以C编写的新内建对象可以通过序列协议中的新槽来定义中in

早期版本的Python使用递归算法删除对象。深度嵌套的数据结构可能导致解释器填充C堆栈并崩溃; Christian Tismer重写了删除逻辑来解决这个问题。在相关的注释,比较递归对象无限递归和崩溃; Jeremy Hylton重写代码不再崩溃,产生一个有用的结果。例如,以下代码后:

a = []
b = []
a.append(a)
b.append(b)

比较a==b返回true,因为两个递归数据结构是同构的。看到线程“trashcan和PR#7”在2000年4月的python-dev邮件列表的档案中讨论导致这个实现,和一些有用的相关链接。注意,比较现在也可以引发异常。在早期版本的Python中,即使用户定义的__cmp__()方法遇到错误,比较操作(如cmp(a,b) ,因为产生的异常将简单地被静默吞咽。

已经完成了将Python移植到安腾处理器上的64位Windows上的工作,主要是ActiveState的Trent Mick。(令人困惑的是,在Win64上,sys.platform仍然是'win32',因为似乎为了方便移植,MS Visual C ++将代码视为32位的安腾。PythonWin也支持Windows CE;有关详细信息,请参阅Python CE页面http://pythonce.sourceforge.net/

另一个新平台是Darwin / MacOS X;最初的支持是在Python 2.0。动态加载工作,如果指定“configure -with-dyld -with-suffix = .x”。有关详细说明,请参阅Python源代码分发中的README。

尝试减轻Python的疣之一,当代码引用局部变量时,常常令人困惑的NameError异常,在变量被赋值之前。例如,以下代码在1.5.2和2.0中的print语句上引发异常;在1.5.2中引发了一个NameError异常,而2.0引发了一个新的UnboundLocalError异常。UnboundLocalErrorNameError的子类,因此任何希望生成NameError的现有代码仍然可以工作。

def f():
    print "i=",i
    i = i + 1
f()

引入了两个新的异常,TabErrorIndentationError它们都是SyntaxError的子类,并且在Python代码发现不正确缩进时引发。

Changes to Built-in Functions

已添加新内建,zip(seq1, seq2, ...)zip()返回一个元组列表,其中每个元组包含来自每个参数序列的第i个元素。zip()映射之间的差异(无, seq1, seq2)map()None中填充序列,如果序列不是全部相同长度,则zip()截断返回列表到最短参数序列的长度。

当第一个参数是字符串时,int()long()函数现在接受可选的“base”参数。int('123', 10) returns 123, while int('123', 16) returns 291. int(123, 16) raises a TypeError exception with the message “can’t convert non-string with explicit base”.

sys模块中添加了一个包含更详细版本信息的新变量。sys.version_info是元组(major, minor, micro, / t6> serial)例如,在假设的2.0.1beta1中,sys.version_info将是> 0, 1, 'beta', 1)level是最终版本的字符串,如"alpha""beta""final"

字典有一个奇怪的新方法,setdefault(key, 默认),其行为类似于现有get()However, if the key is missing, setdefault() both returns the value of default as get() would do, and also inserts it into the dictionary as the value for key. 因此,下面几行代码:

if dict.has_key( key ): return dict[key]
else:
    dict[key] = []
    return dict[key]

可以减少到单个返回 dict.setdefault(key, [])语句。

解释器设置最大递归深度,以便在填充C堆栈并引起核心转储或GPF之前捕获失控递归。以前,当您编译Python时,此限制已修复,但在2.0中,最大递归深度可以使用sys.getrecursionlimit()sys.setrecursionlimit()读取和修改。默认值为1000,通过运行新脚本Misc/find_recursionlimit.py可以找到给定平台的粗略最大值。

Porting to 2.0

新的Python版本努力与以前的版本兼容,并且记录已经相当不错。然而,一些变化被认为是有用的,通常是因为它们修复了初步设计决策,这些决定被证实是被错误地认为,打破向后兼容性并不总是能够避免。本节列出了Python 2.0中可能导致旧的Python代码中断的更改。

可能破坏最多代码的更改会收紧某些方法接受的参数。一些方法将接受多个参数,并将其视为元组,特别是各种列表方法,如append()insert()在早期版本的Python中,如果L是列表,L.append( 1,2 ) t5 >将元组(1,2)附加到列表。在Python 2.0中,这会引发TypeError异常,并显示以下消息:'append require exactly 1 argument; 2给出。修复是简单地添加一组额外的括号以传递这两个值作为元组:L.append( (1,2)

The earlier versions of these methods were more forgiving because they used an old function in Python’s C interface to parse their arguments; 2.0 modernizes them to use PyArg_ParseTuple(), the current argument parsing function, which provides more helpful error messages and treats multi-argument calls as errors. 如果绝对必须使用2.0但不能修复代码,则可以编辑Objects/listobject.c并定义预处理符号NO_STRICT_LIST_APPEND以保留旧的行为;这不是推荐。

socket模块中的某些功能仍然可以通过这种方式。For example, socket.connect( ('hostname', 25) )() is the correct form, passing a tuple representing an IP address, but socket.connect( 'hostname', 25 )() also works. socket.connect_ex()socket.bind()也很容易。2.0alpha1收紧这些功能,但因为文档实际上使用错误的多重参数形式,许多人写的代码,会破坏与更严格的检查。GvR回避了公共反应的变化,因此对于socket模块,文档是固定的,多个参数形式只是标记为deprecated;它在以后的Python版本中再次收紧。

字符串中的\x转义字面值现在只需要2个十六进制数字。以前,它将消耗'x'后面的所有十六进制数字,并取结果的最低8位,因此\x123456等效于\x56

The AttributeError and NameError exceptions have a more friendly error message, whose text will be something like 'Spam' instance has no attribute 'eggs' or name 'eggs' is not defined. 以前的错误消息只是缺少的属性名称eggs,并且写入利用这个事实的代码将在2.0中断。

已经做了一些工作使整数和长整数更可互换。在1.5.2中,为Solaris添加了大文件支持,以允许读取大于2 GiB的文件;这使得文件对象的tell()方法返回一个长整型而不是常规整数。一些代码将减去两个文件偏移量,并尝试使用结果乘以一个序列或切割一个字符串,但这会引发一个TypeError在2.0中,长整数可以用来对序列进行乘法或切片,它的行为将与你直观地期望的一样; 3L * 'abc'产生'abcabcabc',(0,1,2,3)[2L:4L]产生(2,3)。长整数还可以用于以前只接受整数的各种上下文中,例如在文件对象的seek()方法中,以及%操作符(%d%i%x等)。例如,“%d” 2L ** 64将产生字符串18446744073709551616

最小的长整数变化是长整数的str()不再具有尾随的“L”字符,尽管repr()仍然包括它。'L'惹恼了很多人想要打印看起来像普通整数的长整数,因为他们不得不走出去切断字符。这在2.0中不再是问题,但是代码str(longval)[:-1]并假设'L'在那里,现在将丢失最后一位数。

现在,使用浮点的repr()使用与str()不同的格式化精度。repr() uses %.17g format string for C’s sprintf(), while str() uses %.12g as before. 效果是,对于某些数字,repr()可能偶尔显示比str()更多的小数位。例如,数字8.1不能以二进制表示,因此repr(8.1)'8.0999999999999996',而str(8.1)是'8.1'

已删除了将所有标准异常转换为字符串而不是类的-X命令行选项;标准异常现在总是类。包含标准异常的exceptions模块从Python翻译为内建C模块,由Barry Warsaw和Fredrik Lundh编写。

Extending/Embedding Changes

一些更改是隐藏的,并且只有在编写C扩展模块或在更大的应用程序中嵌入Python解释器的人才会显而易见。如果你不处理Python的C API,你可以安全地跳过这一节。

Python C API的版本号增加了,因此为了使用2.0,必须重新编译为1.5.2编译的C扩展。在Windows上,由于Windows DLL的工作方式,Python 2.0无法导入为Python 1.5.x构建的第三方扩展,因此Python会引发异常,导入将失败。

Jim Fulton的ExtensionClass模块的用户将很高兴地发现,添加了钩子,因此ExtensionClasses现在由isinstance()issubclass()支持。这意味着您不再需要记住编写代码,如if type(obj) == myExtensionClass ,但可以使用更自然的if isinstance(obj, myExtensionClass)

Python/importdl.c文件是由大量的#ifdefs支持许多不同平台上的动态加载,由Greg Stein进行了清理和重组。importdl.c现在相当小,平台特定的代码已经移动到一堆Python/dynload_*.c文件中。另一个清理:在Include /目录中还有一些my*.h文件,用于保存各种可移植性;它们已合并到单个文件中,Include/pyport.h

Vladimir Marangozov期待已久的malloc重组已经完成,以便于Python解释器使用自定义分配器而不是C的标准malloc()对于文档,请阅读Include/pymem.hInclude/objimpl.h中的注释。对于长时间的讨论,其中界面被锤炼,请参阅python.org上的'patches'和'python-dev'列表的Web存档。

MacOS的最新版本的GUSI开发环境支持POSIX线程。因此,Python的POSIX线程支持现在可以在Macintosh上运行。还提供了使用用户空间GNU pth库的线程支持。

Windows上的线程支持也得到了增强。Windows支持仅在争用情况下使用内核对象的线程锁;在普通情况下,当没有争用时,他们使用更简单的函数,其速度快一个数量级。NT上的Python 1.5.2的线程版本比无线程版本慢两倍;与2.0的变化,差别只有10%。这些改进是由雅科夫·马可维奇贡献的。

Python 2.0的源现在仅使用ANSI C原型,因此编译Python现在需要一个ANSI C编译器,并且不能再使用只支持K&R C的编译器来完成。

以前,Python虚拟机在其字节码中使用16位数字,限制了源文件的大小。特别是,这影响了Python源中字面值列表和字典的最大大小;偶尔,生成Python代码的人会遇到这种限制。补丁由Charles G. Waldman将限制从2^16引到2^{32}

添加了用于在模块初始化时向模块的字典添加常量的三个新的便利函数:PyModule_AddObject()PyModule_AddIntConstant()PyModule_AddStringConstant()这些函数中的每一个都接受一个模块对象,包含要添加的名称的以null结束的C字符串,以及要分配给该名称的值的第三个参数。第三个参数分别是一个Python对象,一个C长或一个C字符串。

为Unix风格的信号处理程序添加了一个包装API。PyOS_getsig()获取信号处理程序,PyOS_setsig()将设置一个新的处理程序。

Distutils: Making Modules Easy to Install

在Python 2.0之前,安装模块是一件麻烦的事情 - 没有办法自动找出Python的安装位置,或者扩展模块使用什么编译器选项。软件作者不得不经历一个艰难的编辑Makefile和配置文件的仪式,它只在Unix上工作,不支持Windows和MacOS。Python用户面临的安装说明大不相同,这些安装说明在不同的扩展包之间有所不同,这使得管理Python安装变得繁琐。

由Greg Ward管理的配电实用程序的SIG已经创建了Distutils,这是一个使包安装更容易的系统。它们形成distutils包,Python的标准库的一部分。在最好的情况下,从源代码安装Python模块将需要相同的步骤:首先你只需要拆分tarball或zip存档,并运行“python setup.py 安装“。该平台将自动检测,编译器将被识别,C扩展模块将被编译,并将分发安装到正确的目录中。可选的命令行参数提供对安装过程的更多控制,distutils包提供了许多地方来覆盖默认值 - 将构建与安装分离,在非默认目录中构建或安装等。

要使用Distutils,您需要编写一个setup.py脚本。对于简单的情况,当软件只包含.py文件时,最小的setup.py可以只有几行:

from distutils.core import setup
setup (name = "foo", version = "1.0",
       py_modules = ["module1", "module2"])

如果软件包含几个软件包,则setup.py文件并不复杂:

from distutils.core import setup
setup (name = "foo", version = "1.0",
       packages = ["package", "package.subpackage"])

C扩展可能是最复杂的情​​况;这里是从PyXML包中取出的一个例子:

from distutils.core import setup, Extension

expat_extension = Extension('xml.parsers.pyexpat',
     define_macros = [('XML_NS', None)],
     include_dirs = [ 'extensions/expat/xmltok',
                      'extensions/expat/xmlparse' ],
     sources = [ 'extensions/pyexpat.c',
                 'extensions/expat/xmltok/xmltok.c',
                 'extensions/expat/xmltok/xmlrole.c', ]
       )
setup (name = "PyXML", version = "0.5.4",
       ext_modules =[ expat_extension ] )

Distutils还可以负责创建源和二进制分发。由“python setup.py sdist'运行的”sdist“命令构建源分发foo-1.0.tar.gz添加新命令并不困难,“bdist_rpm”和“bdist_wininst”命令已经分别为软件创建RPM分发和Windows安装程序。创建其他分发格式(如Debian软件包和Solaris .pkg文件)的命令处于不同的开发阶段。

所有这一切都记录在一个新的手册分发Python模块中,它连接了一组基本的Python文档。

XML Modules

Python 1.5.2包含了一个简单的XML解析器,由Sjoerd Mullender提供的xmllib模块形式。自1.5.2版本发布以来,用于处理XML的两个不同接口已经变得常见:SAX2(Simple API for XML的版本2)提供了一个事件驱动接口,具有与xmllib文档对象模型)提供了一个基于树的接口,将XML文档转换为可以遍历和修改的节点树。Python 2.0包括一个SAX2接口和一个精简DOM接口,作为xml包的一部分。在这里我们将简要介绍这些新的接口;请参阅Python文档或源代码以获取完整的详细信息。Python XML SIG还在改进文档。

SAX2 Support

SAX定义了用于解析XML的事件驱动接口。要使用SAX,您必须编写一个SAX处理程序类。处理程序类继承自SAX提供的各种类,并覆盖随后将由XML解析器调用的各种方法。For example, the startElement() and endElement() methods are called for every starting and end tag encountered by the parser, the characters() method is called for every chunk of character data, and so forth.

事件驱动方法的优点是,整个文档不必在任何时候驻留在内存中,这在处理真正巨大的文档时很重要。然而,如果你想以一些复杂的方式修改文档结构,写SAX处理程序类可能会非常复杂。

For example, this little example program defines a handler that prints a message for every starting and ending tag, and then parses the file hamlet.xml using it:

from xml import sax

class SimpleHandler(sax.ContentHandler):
    def startElement(self, name, attrs):
        print 'Start of element:', name, attrs.keys()

    def endElement(self, name):
        print 'End of element:', name

# Create a parser object
parser = sax.make_parser()

# Tell it what handler to use
handler = SimpleHandler()
parser.setContentHandler( handler )

# Parse a file!
parser.parse( 'hamlet.xml' )

有关详细信息,请参阅Python文档或XML HOWTO,网址为http://pyxml.sourceforge.net/topics/howto/xml-howto.html

DOM Support

文档对象模型是XML文档的基于树的表示。顶层Document实例是树的根,并且具有作为顶层Element实例的单个子元素。Element具有表示字符数据的子节点和任何子元素,其可以具有它们自己的另外的子元素,等等。使用DOM可以以任何你喜欢的方式遍历结果树,访问元素和属性值,插入和删除节点,并将树转换回XML。

DOM对于修改XML文档很有用,因为您可以创建一个DOM树,通过添加新节点或重新排列子树来修改它,然后生成一个新的XML文档作为输出。您还可以手动构造DOM树并将其转换为XML,这可以是一种更灵活的生成XML输出的方式,而不是简单地写<tag1> ... </tag1>到文件。

Python中包含的DOM实现存在于xml.dom.minidom模块中。它是一个支持XML命名空间的Level 1 DOM的轻量级实现。提供用于生成DOM树的parse()parseString()便利函数:

from xml.dom import minidom
doc = minidom.parse('hamlet.xml')

docDocument实例。Document与所有其他DOM类(例如ElementText一样)是Node基类的子类。因此,DOM树中的所有节点都支持某些常用方法,例如toxml(),它返回包含节点及其子节点的XML表示形式的字符串。每个类也有自己的特殊方法;例如,ElementDocument实例具有查找具有给定标记名称的所有子元素的方法。继续前面的两行示例:

perslist = doc.getElementsByTagName( 'PERSONA' )
print perslist[0].toxml()
print perslist[1].toxml()

对于Hamlet XML文件,上面几行输出:

<PERSONA>CLAUDIUS, king of Denmark. </PERSONA>
<PERSONA>HAMLET, son to the late, and nephew to the present king.</PERSONA>

文档的根元素可作为doc.documentElement提供,并且可以通过删除,添加或删除节点轻松修改其子项:

root = doc.documentElement

# Remove the first child
root.removeChild( root.childNodes[0] )

# Move the new first child to the end
root.appendChild( root.childNodes[0] )

# Insert the new first child (originally,
# the third child) before the 20th child.
root.insertBefore( root.childNodes[0], root.childNodes[20] )

再次,我将引用您到Python文档,以获得不同Node类及其各种方法的完整列表。

Relationship to PyXML

XML特殊兴趣小组一直在开发与XML相关的Python代码。其代码分布,称为PyXML,可从SIG的网页https://www.python.org/community/sigs/current/xml-sig获取。PyXML分发也使用包名称xml如果你编写了使用PyXML的程序,你可能想知道它与2.0 xml包的兼容性。

答案是Python 2.0的xml包与PyXML不兼容,但可以通过安装最新版本的PyXML来兼容。许多应用程序可以使用Python 2.0中包含的XML支持,但更复杂的应用程序将需要安装完整的PyXML包。安装后,PyXML版本0.6.0或更高版本将替换Python附带的xml包,并将是标准包的严格超集,增加了一些额外的功能。PyXML中的一些附加功能包括:

  • 4DOM,一个来自FourThought,Inc.的完整DOM实现
  • xmlproc验证解析器,由Lars Marius Garshol编写。
  • sgmlop解析器加速器模块,由Fredrik Lundh编写。

Module changes

对Python的大量标准库做了大量的改进和修正;一些受影响的模块包括readlineConfigParsercgicalendarposixreadlinexmllibaifcchunk, wave t22>,randomshelvenntplib请参阅CVS日志以获取确切的补丁详细信息。

Brian Gallew为socket模块贡献了OpenSSL支持。OpenSSL是安全套接字层的实现,它加密通过套接字发送的数据。在编译Python时,您可以编辑Modules/Setup以包括SSL支持,它会为socket模块添加一个附加功能:socket.ssl , keyfile, certfile),它接受一个套接字对象并返回一个SSL套接字。httpliburllib模块也已更改为支持https:// URL,但没有人实施了基于SSL的FTP或SMTP。

httplib模块已被Greg Stein重写以支持HTTP / 1.1。提供与1.5版本httplib的向后兼容性,但是使用HTTP / 1.1功能(例如流水线)将需要重写代码以使用不同的接口集合。

Tkinter模块现在支持Tcl / Tk版本8.1,8.2或8.3,并且已经删除了对旧版本7.x版本的支持。Tkinter模块现在支持在Tk小部件中显示Unicode字符串。此外,Fredrik Lundh提供了一个优化,使得像create_linecreate_polygon的操作更快,尤其是在使用大量坐标时。

curses模块已经大大扩展,从Oliver Andrich的增强版本开始,提供了许多ncurses和SYSV curses的附加功能,如颜色,替代字符集支持,pad和鼠标支持。这意味着该模块不再与仅具有BSD curses的操作系统兼容,但似乎没有属于此类别的任何当前维护的操作系统。

如前面讨论的2.0的Unicode支持所提到的,由re模块提供的正则表达式的底层实现已经改变。SRE是Fredrik Lundh撰写的一个新的正规表达式引擎,由Hewlett Packard部分资助,支持对8位字符串和Unicode字符串进行匹配。

New modules

增加了一些新模块。我们将简单地列出他们简要的描述;有关特定模块的详细信息,请参阅2.0文档。

  • atexit:用于注册要在Python解释器退出之前调用的函数。当前设置sys.exitfunc的代码应改为使用atexit模块,导入atexit并调用atexit.register()与退出时调用的函数。(供稿:Skip Montanaro。)
  • codecsencodingsunicodedata:作为新的Unicode支持的一部分添加。
  • filecmp:取代旧的cmpcmpcachedircmp模块,现已弃用。(供稿:Gordon MacMillan和Moshe Zadka。)
  • gettext:此模块通过为GNU gettext消息目录库提供接口,为Python程序提供国际化(I18N)和本地化(L10N)支持。(由巴里华沙综合,由马丁·冯·洛维斯,彼得·福克和詹姆斯·亨斯特里奇的独立贡献。)
  • linuxaudiodev:支持Linux上的/dev/audio设备,是现有sunaudiodev模块的双胞胎。(由Peter Bosch提供,由Jeremy Hylton修订。)
  • mmap:Windows和Unix上内存映射文件的接口。文件的内容可以直接映射到内存,此时它的行为就像一个可变字符串,所以它的内容可以被读取和修改。它们甚至可以传递给期望普通字符串的函数,例如re模块。(由Sam Rushing提供,有一些扩展名。Kuchling。)
  • pyexpat:Expat XML解析器的接口。(由Paul Prescod提供。)
  • robotparser:解析robots.txt文件,用于编写有礼避免网站某些区域的Web蜘蛛。解析器接受robots.txt文件的内容,从中构建一组规则,然后可以回答有关给定URL的可抓取性的问题。(供稿:Skip Montanaro。)
  • tabnanny:用于检查Python源代码是否存在歧义缩进的模块/脚本。(由Tim Peters提供。)
  • UserString:用于导出行为类似字符串的对象的基类。
  • webbrowser:一个模块,提供了一种独立于平台的方式来在特定URL上启动Web浏览器。对于每个平台,以特定顺序尝试各种浏览器。用户可以通过设置BROWSER环境变量来更改启动的浏览器。(最初的灵感来自Eric S. Raymond对urllib的补丁,它添加了类似的功能,但最终模块来自Fred Drake最初实现的代码,因为Tools/idle/BrowserControl.py ,并适应了Fred的标准库)。
  • _winreg:Windows注册表的接口。_winreg是自1995年以来一直作为PythonWin的一部分的函数的修改,但现在已经添加到核心分布,并增强了支持Unicode。_winreg由Bill Tutt和Mark Hammond编写。
  • zipfile:用于读取和写入ZIP格式的存档的模块。这些是由DOS / Windows上的PKZIP或Unix上的zip生成的归档,不要与gzip -format文件gzip模块)(由James C.提供Ahlstrom。)
  • imputil:与现有的ihooks模块相比,提供了一种更简单的方式来编写自定义导入钩子的模块。(由Greg Stein实施,一路上对python-dev有很多讨论。)

IDLE Improvements

IDLE是使用Tkinter编写的官方Python跨平台IDE。Python 2.0包括IDLE 0.6,它增加了一些新的功能和改进。部分列表:

  • UI改进和优化,特别是在语法高亮和自动缩进方面。
  • 类浏览器现在显示更多信息,例如模块中的顶层函数。
  • 标签宽度现在是用户可设置的选项。打开现有的Python文件时,IDLE会自动检测缩进约定并进行自适应。
  • 现在支持在各种平台上调用浏览器,用于在浏览器中打开Python文档。
  • IDLE现在有一个命令行,这在很大程度上类似于香草Python解释器。
  • 在许多地方添加了通话提示。
  • IDLE现在可以作为包安装。
  • 在编辑器窗口中,底部现在有一个线/列栏。
  • 三个新的击键命令:检查模块(Alt-F5),导入模块(F5)和运行脚本(Ctrl-F5)。

Deleted and Deprecated Modules

一些模块已被删除,因为它们已过时,或者因为现在有更好的方法来做同样的事情。stdwin模块已消失;它是一个不再发展的平台无关的窗口工具包。

A number of modules have been moved to the lib-old subdirectory: cmp, cmpcache, dircmp, dump, find, grep, packmail, poly, util, whatsound, zmod. 如果您有依赖于已移动到lib-old的模块的代码,您可以将该目录添加到sys.path,以便返回,鼓励更新使用这些模块的任何代码。

Acknowledgements

作者感谢以下人士就本文的各种草案提出建议:David Bolen,Mark Hammond,Gregg Hauser,Jeremy Hylton,Fredrik Lundh,Detlef Lannert,Aahz Maruch,Skip Montanaro,Vladimir Marangozov,Tobias Polzin,Guido van Rossum,Neil Schemenauer和Russ Schmidt。