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 name 或 from 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()

两个新的例外, TabError 和 IndentationError ,已经介绍。它们都是 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 .

这个 AttributeError 和 NameError 异常有一个更友好的错误消息,其文本如下 '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.h 和 Include/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^16 到 2^{{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类一样,例如 Element 和 Text ,是 Node 基类。因此,DOM树中的所有节点都支持某些常用方法,例如 toxml() 返回包含节点及其子节点的XML表示形式的字符串。每个类也有自己的特殊方法;例如, Element 和 Document 实例具有查找具有给定标记名的所有子元素的方法。从前面的两行示例继续:

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广泛的标准库进行了许多改进和错误修复;一些受影响的模块包括 readline , ConfigParser , cgi , calendar , posix , readline , xmllib , aifc , chunk, wave , random , shelve 和 nntplib .请参阅cvs日志以了解补丁的详细信息。

Brian Gallew为 socket 模块。OpenSSL是安全套接字层的一种实现,它对通过套接字发送的数据进行加密。编译python时,可以编辑 Modules/Setup 要包括SSL支持,它将向 socket 模块: socket.ssl(socket, keyfile, certfile) ,它接受一个socket对象并返回一个ssl套接字。这个 httplib 和 urllib 模块也被更改为支持 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_line 和 create_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提供。)

  • codecs , encodings , unicodedata :作为新Unicode支持的一部分添加。

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

  • 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 子目录: cmp , cmpcache , dircmp , dump , find , grep , packmail , poly , util , whatsound , zmod . 如果您的代码依赖于已移动到的模块 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。