python 2.0的新功能

作者

A.M.Kuchling和Moshe Zadka

介绍

2000年10月16日发布了一个新版本的python,版本2.0。本文介绍了2.0中令人兴奋的新特性,重点介绍了一些其他有用的更改,并指出了一些不兼容的更改,这些更改可能需要重写代码。

python的开发从来没有在发布之间完全停止过,并且始终提交稳定的bug修复和改进流。许多小的修正、一些优化、附加的docstring和更好的错误消息都进入了2.0;列出它们都是不可能的,但它们确实很重要。如果您想查看完整的列表,请查阅公开的CVS日志。这一进展是由于为pythonlabs工作的五个开发人员花了一天的时间来修复bug而获得报酬的,同时也是由于迁移到sourceforge后改进了通信。

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树收到了一些修复,以确保它与Python2.0向前兼容。因此,1.6是Python进化的一部分,而不是分支。

那么,您应该对python 1.6感兴趣吗?大概不会。1.6最终版本和2.0beta1版本是在同一天(2000年9月5日)发布的,计划在大约一个月内完成python 2.0。如果你有需要维护的应用程序,那么通过移动到1.6,修复它们,然后在一个月内移动到2.0,再进行一轮破坏似乎没有什么意义;你最好直接转到2.0。本文档中描述的大多数真正有趣的功能都只在2.0中,因为在5月到9月之间做了很多工作。

新开发过程

python 2.0中最重要的变化可能根本不是代码,而是如何开发python:2000年5月,python开发人员开始使用sourceforge提供的工具来存储源代码、跟踪bug报告和管理补丁提交队列。要为python 2.0报告错误或提交修补程序,请使用python的项目页面(位于https://sourceforge.net/project s/python/)提供的错误跟踪和修补程序管理器工具。

现在托管在sourceforge的最重要的服务是python cvs树,它是一个版本控制的存储库,包含python的源代码。以前,大约有7个人可以写入cvs树,所有补丁都必须由这个短列表中的一个人检查和签入。显然,这不是非常可扩展的。通过将cvs树移到sourceforge,可以向更多人授予写访问权限;截至2000年9月,有27人能够签入更改,增加了四倍。这使得大规模的更改成为可能,如果需要通过一小群核心开发人员进行筛选,就不会尝试进行这种更改。例如,有一天,Peter Schneider Kamp突然想到放弃K&R C兼容性,将Python的C源代码转换为ANSI C。在获得了python dev邮件列表的批准后,他启动了一系列的签入,持续了大约一周,其他开发人员也加入了帮助,工作完成了。如果只有5个人具有写访问权限,那么该任务可能会被视为“不错,但不值得花费所需的时间和精力”,而且永远也不会完成。

使用SourceForge服务的转变导致了开发速度的显著提高。补丁现在被提交、评论、由原始提交者以外的人修改,并且在人们之间来回弹回,直到补丁被认为值得检查为止。Bug在一个中心位置被跟踪,并可以分配给特定的人进行修复,我们可以计算打开的Bug数量来衡量进度。这并非没有代价:开发人员现在有更多的电子邮件要处理,有更多的邮件列表要遵循,并且必须为新环境编写特殊的工具。例如,sourceforge发送了默认的补丁和错误通知电子邮件,这些邮件是完全无用的,所以ka-ping-yee编写了一个HTML屏幕抓取程序,可以发送更有用的消息。

添加代码的简单性导致了一些最初的增长难题,例如代码在准备好之前就签入了,或者没有得到开发人员组的明确同意。出现的审批流程与Apache Group使用的流程有些相似。开发人员可以在一个补丁上投票+1、+0、-0或-1;+1和-1表示接受或拒绝,而+0和-0意味着开发人员对更改基本上是漠不关心的,尽管有轻微的正或负倾斜。与阿帕奇模式相比,最显著的变化是投票本质上是咨询性的,让拥有仁慈独裁者身份的吉多·范·罗森知道一般的意见是什么。他仍然可以忽略投票结果,即使社区不同意他,他也可以批准或拒绝变更。

生成一个实际的补丁是添加新特性的最后一步,与之前提出一个好的设计的任务相比,通常很容易。对新特性的讨论常常会爆发成冗长的邮件列表线程,这使得讨论很难进行,而且没有人能够阅读到每个发布到python-dev的文章。因此,已经建立了一个相对正式的过程来编写python增强建议(peps),该过程是以Internet RFC过程为模型的。PEP是描述一个提议的新特征的文件草稿,并且不断地修改,直到社区达成共识,或者接受或者拒绝这个提议。引自引言 PEP 1 “PEP的目的和指导方针”:

PEP代表python增强方案。PEP是一个向Python社区提供信息或描述Python新特性的设计文档。PEP应提供功能的简明技术规范和功能的基本原理。

我们希望PEPS是提出新特性、收集社区对某个问题的输入以及记录进入Python的设计决策的主要机制。PEP负责在社区内建立共识,并记录不同意见。

阅读 PEP 1 有关PEP编辑流程、风格和格式的详细信息。PEP保存在sourceforge上的python cvs树中,尽管它们不是python 2.0发行版的一部分,也可以从https://www.python.org/dev/peps/以HTML格式提供。截至2000年9月,有25个PEP,范围从 PEP 201 “lockstep迭代”,到PEP 225,“elementwise/objectwise运算符”。

unicode

python 2.0中最大的新特性是一种新的基本数据类型:unicode字符串。Unicode使用16位数字来表示字符,而不是ASCII使用的8位数字,这意味着可以支持65536个不同的字符。

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字符串具有 encode( [encoding] ) 方法,返回所需编码中的8位字符串。编码是由字符串命名的,例如 'ascii''utf-8''iso-8859-1' 或者别的什么。编解码器API是为实现和注册新的编码而定义的,这些编码随后可在整个Python程序中使用。如果未指定编码,则默认编码通常为7位ASCII,但可以通过调用 sys.setdefaultencoding(encoding) 自定义版本中的函数 site.py .

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

已添加新的内置函数,并修改现有的内置函数以支持Unicode:

  • unichr(ch) 返回一个长度为1个字符的Unicode字符串,其中包含 ch .

  • ord(u) 在哪里 u 是1个字符的正则字符串或Unicode字符串,以整数形式返回字符数。

  • unicode(string [, encoding]  [, errors] ) 从8位字符串创建Unicode字符串。 encoding 是一个命名要使用的编码的字符串。这个 errors 参数指定对当前编码无效的字符的处理方式;传递 'strict' 因为该值导致在任何编码错误上引发异常,而 'ignore' 导致错误被静默忽略,并且 'replace' 如果有任何问题,请使用u+fffd(官方替换字符)。

  • 这个 exec 语句,以及各种内置的 eval()getattr()setattr() 也将接受Unicode字符串和常规字符串。(修复此问题的过程可能遗漏了一些内置函数;如果您发现一个接受字符串但根本不接受Unicode字符串的内置函数,请将其报告为bug。)

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

这个 codecs 模块包含查找现有编码和注册新编码的函数。除非您想要实现一个新的编码,否则您将经常使用 codecs.lookup(encoding) 函数,返回4元素元组: (encode_func, decode_func, stream_reader, stream_writer) .

  • encode_func 是一个接受Unicode字符串并返回2元组的函数 (string, length) . string 是一个8位字符串,包含转换为给定编码的Unicode字符串的一部分(可能全部),以及 长度 告诉您Unicode字符串转换了多少。

  • decode_func 与…相反 encode_func ,获取8位字符串并返回2元组 (ustring, length) ,由生成的Unicode字符串组成 护城河 整数 长度 说明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()

支持Unicode的正则表达式可通过 re 模块,它有一个新的底层实现,称为SRE,由秘密实验室AB的Fredrik Lundh编写。

A -U 添加了命令行选项,使Python编译器将所有字符串文本解释为Unicode字符串文本。这是为了在测试和将来验证您的python代码时使用,因为将来的一些版本的python可能会放弃对8位字符串的支持,而只提供unicode字符串。

列表推导

列表是Python中的Workhorse数据类型,许多程序在某个时刻操纵列表。列表上的两个常见操作是循环它们,要么选择满足某个标准的元素,要么对每个元素应用一些函数。例如,给定一个字符串列表,您可能希望拉出包含给定子字符串的所有字符串,或者从每行中去掉尾随空格。

现有的 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 表达式知道正在搜索什么子字符串。列表理解使此更清晰:

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 ]

这个 for …… in 子句包含要迭代的序列。序列不一定是相同的长度,因为它们是 not 并行迭代,但从左到右;这在下面的段落中解释得更清楚。生成的列表的元素将是 表达 . 决赛 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.

这意味着当存在多个 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保持最新。

增广的任务

另一个长期被要求的特性,增强的赋值操作符,已经被添加到python 2.0中。增强的赋值运算符包括 +=-=*= 等等。例如,语句 a += 2 增加变量的值 a 乘以2,相当于稍长的 a = a + 2 .

支持的分配运算符的完整列表为 +=-=*=/=%=**=&=|=^=>>=<<= . 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 模块,通常是 strop 用C编写的模块。添加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方法返回新的字符串,并且不修改它们所操作的字符串。

老年人 string 模块仍然具有向后兼容性,但它主要充当新字符串方法的前端。

在2.0之前的版本中没有并行的两种方法,尽管它们在jpython中存在了相当长的一段时间,但是它们是 startswith()endswith() . s.startswith(t) 等于 s[:len(t)] == t ,同时 s.endswith(t) 等于 s[-len(t):] == t .

另一个值得特别提及的方法是 join() . 这个 join() 字符串的方法接收一个参数、字符串序列,并等效于 string.join() 旧款功能 string 模块,参数颠倒。换言之, s.join(seq) 相当于旧的 string.join(seq, s) .

循环垃圾收集

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

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

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

instance = SomeClass()
instance.myself = instance

执行上述两行代码后,参考计数为 instance 是2;一个引用来自名为 'instance' 另一个来自 myself 实例的属性。

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

python 2.0通过定期执行循环检测算法来解决这个问题,该算法查找不可访问的循环并删除相关的对象。一个新的 gc 模块提供执行垃圾收集、获取调试统计信息和调整收集器参数的功能。

运行周期检测算法需要一些时间,因此会导致一些额外的开销。希望在我们从使用2.0中获得使用周期集合的经验之后,python 2.1能够通过仔细的调优将开销最小化。目前还不清楚会损失多少性能,因为基准测试很复杂,而且关键取决于程序创建和销毁对象的频率。在编译python时,如果您甚至不能承受很小的速度损失,或者通过指定 --without-cycle-gc 运行时切换 configure 脚本。

有几个人解决了这个问题,并为解决这个问题作出了贡献。Toby Kelsey编写了周期检测方法的早期实现。Eric Tiedemann在访问CNRI时提出了当前的算法,Guido van Rossum和Neil Schemenauer编写了两个不同的实现,之后由Neil集成。在这一过程中,很多人都提出了建议;2000年3月的python dev邮件列表档案包含了大部分相关的讨论,特别是在标题为“为python收集参考周期”和“再次完成”的线程中。

其他核心变更

对python的语法和内置函数进行了各种细微的更改。这些变化都不是很深远,但它们是方便的。

次要语言更改

新的语法使用参数元组和/或关键字参数字典调用给定函数更加方便。在python 1.5及更早版本中,您将使用 apply() 内置功能: apply(f, args, kw) 调用函数 f() 用参数tuple args 以及字典中的关键字参数 kw . apply() 在2.0中是一样的,但是多亏了Greg Ewing的一个补丁, f(*args, **kw) 是一种更短更清晰的方法来达到同样的效果。此语法与定义函数的语法对称:

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

这个 print 语句的输出现在可以指向类似文件的对象,方法是 print 具有 >> file 类似于Unix shell中的重定向操作符。以前你要么用 write() 类文件对象的方法,缺乏 print ,或者可以将新值赋给 sys.stdout 然后恢复旧值。对于将输出发送到标准错误,更容易编写:

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

模块现在可以在导入时使用语法重命名。 import module as namefrom module import name as othername . 补丁是由托马斯·沃特斯提交的。

当使用 % 运算符;“%r”将插入 repr() 它的参数。这也是从对称性考虑中添加的,这次是为了与现有的格式样式“%s”对称,该格式样式插入了 str() 它的参数。例如, '%r %s' % ('abc', 'abc') 返回一个包含 'abc' abc .

以前没有办法实现一个超越Python内置的类 in 并实现了一个自定义版本。 obj in seq 返回true obj 出现在序列中 seq ;python只需尝试序列的每个索引,直到 obj 被发现或被发现 IndexError 遇到。Moshe Zadka贡献了一个补丁,增加了 __contains__() 为提供自定义实现的魔力方法 in .此外,用C编写的新内置对象可以定义 in 指通过序列协议中的新插槽为它们提供。

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

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

比较 a==b 返回true,因为这两个递归数据结构是同构的。请参阅python dev邮件列表的2000年4月存档中的线程“垃圾桶和pr_7”,以了解此实现之前的讨论,以及一些有用的相关链接。请注意,比较现在也可以引发异常。在早期版本的python中,比较操作,如 cmp(a,b) 即使是用户定义的 __cmp__() 方法遇到错误,因为生成的异常将被静默接受。

在将python移植到Itanium处理器上的64位窗口上已经做了很多工作,主要是由ActiveState的TrentMick完成的。(令人困惑地, sys.platform 仍然是 'win32' 在WIN64上,因为为了便于移植,MS VisualC++将代码视为ItAuthic上的32位。PythOnWin还支持Windows CE;请查看HTTP://PythOn.SooSurfGe.NET/Python CE页面以获取更多信息。

另一个新平台是darwin/macos x;对它的最初支持是在python 2.0中。如果指定“configure--with dyld--with suffix=.x”,动态加载就可以工作。有关更多说明,请参阅python源代码发行版中的自述文件。

人们试图减轻 Python 的一个疣,这种疣经常让人困惑。 NameError 在为变量赋值之前,代码引用局部变量时出现异常。例如,以下代码在 print 1.5.2和2.0中的声明;1.5.2 a中的声明 NameError 引发异常,而2.0引发新的 UnboundLocalError 例外。 UnboundLocalError 是的子类 NameError ,所以需要 NameError 待抚养的子项仍然可以工作。地址:

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

两个新的例外, TabErrorIndentationError ,已经介绍。它们都是 SyntaxError ,并在发现Python代码缩进不正确时引发。

对内置函数的更改

一个新的内置的, zip(seq1, seq2, ...) ,已添加。 zip() 返回一个元组列表,其中每个元组包含每个参数序列中的第i个元素。两者之间的区别 zip()map(None, seq1, seq2) 那是 map() 在序列中添加 None 如果序列的长度不一致, zip() 将返回的列表截断为最短参数序列的长度。

这个 int()long() 当第一个参数是字符串时,函数现在接受一个可选的“base”参数。 int('123', 10) 返回123,而 int('123', 16) 返回291。 int(123, 16) 提高 TypeError 消息“can't convert non string with explicit base”出现异常。

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

字典有一种奇怪的新方法, setdefault(key, default) ,其行为类似于现有的 get() 方法。但是,如果钥匙丢失, setdefault() 两者都返回 default 作为 get() 可以,并将其作为的值插入字典 key . 因此,以下代码行:

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

可以简化为单个 return dict.setdefault(key, []) 语句。

解释器设置最大递归深度,以便在填充C堆栈并导致核心转储或GPF之前捕获失控的递归。以前在编译python时这个限制是固定的,但是在2.0中,可以使用 sys.getrecursionlimit()sys.setrecursionlimit() . 默认值为1000,通过运行新脚本可以找到给定平台的大致最大值, Misc/find_recursionlimit.py .

移植到2.0

新的python版本试图与以前的版本兼容,而且记录非常好。然而,一些变更被认为是足够有用的,通常是因为它们修复了最初的设计决策,结果发现这些决策是主动错误的,破坏向后兼容性并不总是可以避免的。本节列出了python 2.0中可能导致旧python代码中断的更改。

可能会破坏大多数代码的更改是收紧某些方法接受的参数。有些方法会采用多个参数,并将它们视为一个元组,特别是各种列表方法,例如 append()insert() . 在早期版本的python中,如果 L 是一个列表, L.append( 1,2 ) 附加元组 (1,2) 到列表中。在python 2.0中,这会导致 TypeError 将引发异常,并显示消息:“append正好需要1个参数;已给定2个”。解决方法是简单地添加一组额外的圆括号,将两个值作为元组传递: L.append( (1,2) ) .

这些方法的早期版本更容易理解,因为它们在Python的C接口中使用了一个旧函数来解析它们的参数;2.0使它们更现代化以使用 PyArg_ParseTuple() 当前参数解析函数,它提供更多有用的错误消息,并将多参数调用视为错误。如果必须使用2.0但无法修复代码,则可以编辑 Objects/listobject.c 并定义预处理器符号 NO_STRICT_LIST_APPEND 为了保持旧的行为,不建议这样做。

中的一些函数 socket 模块仍然可以这样原谅。例如, socket.connect( ('hostname', 25) )() 是正确的形式,传递表示IP地址的元组,但是 socket.connect( 'hostname', 25 )() 同样有效。 socket.connect_ex()socket.bind() 同样容易相处。2.0alpha1加强了这些功能,但由于文档实际上使用了错误的多参数形式,许多人编写了代码,这将打破更严格的检查。在面对公众的反应时,全球汽车规则拒绝了这些变化,因此 socket 模块,文档已修复,多参数窗体仅标记为已弃用;它 will 在将来的python版本中再次收紧。

这个 \x 字符串文本中的转义现在只需要2个十六进制数字。以前,它会使用“x”后面的所有十六进制数字,并获取结果的最低8位,所以 \x123456 相当于 \x56 .

这个 AttributeErrorNameError 异常有一个更友好的错误消息,其文本如下 'Spam' instance has no attribute 'eggs'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() 使用 %.17g C的格式字符串 sprintf() ,同时 str() 使用 %.12g 像以前一样。结果是 repr() 有时显示的小数位数可能比 str() ,对于某些数字。例如,数字8.1不能精确地用二进制表示,所以 repr(8.1)'8.0999999999999996' ,而str(8.1)是 '8.1' .

这个 -X 命令行选项已被删除,它将所有标准异常转换为字符串而不是类;标准异常现在总是类。这个 exceptions 包含标准异常的模块从python转换为内置的C模块,由BarryWarsaw和FredrikLundh编写。

扩展/嵌入更改

其中一些更改是隐藏的,只有编写C扩展模块或在更大的应用程序中嵌入一个Python解释器的人才能看到。如果您不处理Python的C API,可以安全地跳过这一部分。

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

Jim Fulton的extensionclass模块的用户会很高兴地发现,已经添加了钩子,因此extensionclass现在得到了 isinstance()issubclass() . 这意味着您不再需要记住编写诸如 if type(obj) == myExtensionClass 但可以使用更自然的 if isinstance(obj, myExtensionClass) .

这个 Python/importdl.c 这个文件是大量的ifdef来支持在许多不同平台上的动态加载,由greg stein清理并重新组织。 importdl.c 现在相当小,平台特定的代码已经移动到 Python/dynload_*.c 文件夹。另一个清理:还有一些 my*.h 包含各种可移植性黑客的include/目录中的文件;它们已合并为单个文件, Include/pyport.h .

Vladimir Marangozov期待已久的malloc重组已经完成,为了便于让python解释器使用自定义分配器而不是C的标准 malloc() . 有关文档,请阅读 Include/pymem.hInclude/objimpl.h . 关于界面开发过程中的冗长讨论,请参见python.org上“补丁”和“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 c原型,所以编译python现在需要一个ansi c c编译器,而不能再使用只支持k&r c的编译器。

以前,python虚拟机在字节码中使用16位数字,限制了源文件的大小。特别是,这会影响python源代码中文字列表和字典的最大大小;偶尔生成python代码的人会遇到这种限制。查尔斯·G·瓦尔德曼的一个补丁把极限从 2^162^{{32}} .

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

为UNIX样式的信号处理程序添加了封装API。 PyOS_getsig() 获取信号处理程序并 PyOS_setsig() 将设置新的处理程序。

distutils:使模块易于安装

在python 2.0之前,安装模块是一件乏味的事情——没有办法自动确定在哪里安装了python,或者为扩展模块使用什么编译器选项。软件作者必须经历一个艰难的过程,即编辑makefile和配置文件,这些文件只在Unix上工作,不支持Windows和MacOS。python用户面临着各种各样的安装指令,这些指令在不同的扩展包之间有所不同,这使得管理python安装有些麻烦。

由GregWard领导的用于分发实用程序的SIG创建了distutils,一个使包安装更加容易的系统。它们构成了 distutils 包,是Python标准库的一个新部分。在最好的情况下,从源代码安装一个python模块需要相同的步骤:首先,您的意思只是解包tarball或zip归档文件,然后运行“python setup.py install`”。将自动检测平台,识别编译器,编译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 ] )

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

所有这些都记录在新手册中, 分发python模块 ,它连接了基本的Python文档集。

XML模块

python 1.5.2以 xmllib 模块,由Sjoerd Mullender提供。自1.5.2发布以来,处理XML的两个不同接口已经变得很常见:SAX2(SimpleAPI for XML的版本2)提供了一个事件驱动接口,与 xmllib 和DOM(文档对象模型)提供了一个基于树的接口,将XML文档转换为一个可以遍历和修改的节点树。python 2.0包括一个sax2接口和一个剥离的dom接口,作为 xml 包裹。在这里,我们将简要概述这些新接口;有关完整的详细信息,请参阅Python文档或源代码。python XML SIG也在研究改进的文档。

SAX2支撑

SAX定义了用于解析XML的事件驱动接口。要使用SAX,必须编写SAX处理程序类。处理程序类继承自SAX提供的各种类,并重写XML解析器随后将调用的各种方法。例如, startElement()endElement() 对解析器遇到的每个开始和结束标记调用方法, characters() 方法是为每个字符数据块调用的,等等。

事件驱动方法的优点是,整个文档在任何时候都不必驻留在内存中,这对于处理真正庞大的文档很重要。但是,如果您试图以某种详细的方式修改文档结构,那么编写SAX处理程序类可能会变得非常复杂。

例如,这个小示例程序定义了一个处理程序,它为每个开始和结束标记打印一条消息,然后解析该文件。 hamlet.xml 使用:

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文档,或http://pyxml.sourceforge.net/topics/howto/xml-howto.html上的xml howto。

DOM支持

文档对象模型是基于树的XML文档表示。顶层 Document 实例是树的根,它有一个子级,即顶层 Element 实例。这个 Element 具有表示字符数据和任何子元素的子节点,这些子元素可能有自己的子元素,等等。使用DOM,您可以随意遍历生成的树,访问元素和属性值,插入和删除节点,并将树转换回XML。

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

包含在python中的dom实现存在于 xml.dom.minidom 模块。它是级别1 DOM的轻量级实现,支持XML名称空间。这个 parse()parseString() 提供了生成DOM树的方便函数:

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

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

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

对于 哈姆雷特 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 类及其各种方法。

与pyxml的关系

XML特殊兴趣小组已经研究了一段时间与XML相关的Python代码。其代码分发名为pyxml,可从sig的网页https://www.python.org/community/sig s/current/xml-sig获得。pyxml分发还使用包名称 xml . 如果您已经编写了使用pyxml的程序,那么您可能会想知道它与2.0的兼容性。 xml 包裹。

答案是python 2.0的 xml 包与pyxml不兼容,但可以通过安装最新版本的pyxml使其兼容。许多应用程序都可以通过python 2.0中包含的XML支持,但更复杂的应用程序需要安装完整的pyxml包。安装后,pyxml版本0.6.0或更高版本将替换 xml 随python一起提供的包,将是一个严格的标准包的超集,添加了一系列附加功能。pyxml中的一些附加功能包括:

  • 4DOM,来自Fourthought,Inc.的完整DOM实现。

  • xmlproc验证解析器,由lars marius garshol编写。

  • 这个 sgmlop 解析器加速模块,由Fredrik Lundh编写。

模块变更

对python广泛的标准库进行了许多改进和错误修复;一些受影响的模块包括 readlineConfigParsercgicalendarposixreadlinexmllibaifcchunk, waverandomshelvenntplib .请参阅cvs日志以了解补丁的详细信息。

Brian Gallew为 socket 模块。OpenSSL是安全套接字层的一种实现,它对通过套接字发送的数据进行加密。编译python时,可以编辑 Modules/Setup 要包括SSL支持,它将向 socket 模块: socket.ssl(socket, keyfile, certfile) ,它接受一个socket对象并返回一个ssl套接字。这个 httpliburllib 模块也被更改为支持 https:// 虽然没有人通过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的附加功能,如颜色、可选字符集支持、pads和鼠标支持。这意味着模块不再与只有BSDcurses的操作系统兼容,但似乎没有任何当前维护的OSE属于此类。

正如前面讨论的2.0的Unicode支持中所提到的,由 re 模块已更改。SRE是一个新的正则表达式引擎,由Fredrik Lundh编写,部分由Hewlett-Packard资助,它支持对8位字符串和Unicode字符串进行匹配。

新模块

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

  • atexit :用于注册要在Python解释器退出之前调用的函数。当前设置的代码 sys.exitfunc 应直接更改为使用 atexit 而不是模块,导入 atexit 呼唤 atexit.register() 在退出时调用函数。(由Skip Montanaro提供。)

  • codecsencodingsunicodedata :作为新Unicode支持的一部分添加。

  • filecmp :取代旧的 cmpcmpcachedircmp 模块,现在已弃用。(由戈登·麦克米伦和莫什·扎达卡提供。)

  • gettext :此模块通过向GNU GetText消息目录库提供接口,为python程序提供国际化(i18n)和本地化(l10n)支持。(由巴里·华沙(Barry Warsaw)整合,马丁·冯·卢维斯(Martin von L_wis)、彼得·芬克(Peter Funk)和詹姆斯·亨斯特里奇(James Henstridge)分别贡献。)

  • linuxaudiodev :支持 /dev/audio Linux上的设备,现有的一对 sunaudiodev 模块。(由彼得·博什提供,杰里米·希尔顿提供修复。)

  • mmap :Windows和Unix上内存映射文件的接口。一个文件的内容可以直接映射到内存中,此时它的行为就像一个可变的字符串,所以它的内容可以被读取和修改。它们甚至可以传递给期望普通字符串的函数,例如 re 模块。(萨姆·拉什的贡献,还有A.M.库奇林的一些扩展。)

  • pyexpat :expat XML解析器的接口。(由Paul Prescod提供。)

  • robotparser :分析A robots.txt 文件,用于编写Web蜘蛛,礼貌地避开网站的某些区域。解析器接受 robots.txt 文件,从中构建一组规则,然后可以回答有关给定URL可获取性的问题。(由Skip Montanaro提供。)

  • tabnanny :一个模块/脚本,用于检查python源代码是否存在不明确的缩进。(蒂姆·彼得斯供稿)

  • UserString :用于派生行为类似字符串的对象的基类。

  • webbrowser :提供独立于平台的方式在特定URL上启动Web浏览器的模块。对于每个平台,不同的浏览器按特定的顺序进行尝试。用户可以通过设置 BROWSER 环境变量。(最初的灵感来自埃里克·S·雷蒙德的补丁 urllib 它增加了类似的功能,但最终的模块来自于最初由FredDrake实现的代码 Tools/idle/BrowserControl.py 并改编为弗雷德的标准类库。)

  • _winreg :Windows注册表的接口。 _winreg 是对自1995年以来作为pythonwin一部分的函数的一种改编,但现在已添加到核心发行版中,并增强以支持Unicode。 _winreg 是比尔·塔特和马克·哈蒙德写的。

  • zipfile :用于读取和写入zip格式存档的模块。这些档案是由 PKZIP 在DOS/Windows或 zip 在Unix上,不要混淆 gzip -格式化文件(由 gzip 模块(由James C.Ahlstrom贡献)

  • imputil :一个模块,与现有模块相比,它为编写自定义导入挂钩提供了一种更简单的方法。 ihooks 模块。(由GregStein实现,沿途有很多关于python dev的讨论。)

闲置改进

idle是正式的python跨平台IDE,使用tkinter编写。python 2.0包括idle 0.6,这增加了一些新的特性和改进。部分列表:

  • 用户界面的改进和优化,特别是在语法突出显示和自动缩进方面。

  • 类浏览器现在显示更多信息,例如模块中的顶级函数。

  • 选项卡宽度现在是用户可设置的选项。打开现有的python文件时,idle会自动检测缩进约定并进行调整。

  • 现在支持在各种平台上调用浏览器,用于在浏览器中打开Python文档。

  • idle现在有一个命令行,这在很大程度上类似于普通的python解释器。

  • 许多地方都增加了通话提示。

  • 现在可以将idle作为包安装。

  • 在编辑器窗口中,底部现在有一个行/列栏。

  • 三个新的按键命令:检查模块 (Alt-F5 )导入模块 (F5 )运行脚本 (Ctrl-F5

已删除和已弃用的模块

一些模块被丢弃是因为它们已经过时了,或者因为现在有更好的方法来做同样的事情。这个 stdwin 模块已经不存在了;它是为一个不再开发的独立于平台的窗口工具包开发的。

一些模块已移动到 lib-old 子目录: cmpcmpcachedircmpdumpfindgreppackmailpolyutilwhatsoundzmod . 如果您的代码依赖于已移动到的模块 lib-old ,只需将该目录添加到 sys.path 为了让它们返回,但是我们鼓励您更新任何使用这些模块的代码。

确认

作者要感谢以下人士对本文的各种草稿提出建议: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。