Python 2.0 新增Function

  • Author

    • 上午。 Kuchling 和 Moshe Zadka

Introduction

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

在发行版之间,Python 的开发永远不会完全停止,并且总是不断提交错误修复和改进。 2.0 中引入了许多小的修补程序,一些优化,附加的文档字符串和更好的错误消息。列出所有这些都将是不可能的,但是它们无疑是重要的。如果要查看完整列表,请查阅公开提供的 CVS 日志。取得这一进展的原因是,为 PythonLabs 工作的五名开发人员现在已获得报酬来花费其时间来修复错误,也归因于迁移到 SourceForge 带来的通信改善。

Python 1.6 呢?

可以将 Python 1.6 视为“Contract 义务” Python 版本。在核心开发团队于 2000 年 5 月离开 CNRI 之后,CNRI 要求创建一个 1.6 版本,其中包含在 CNRI 上执行的有关 Python 的所有工作。因此,Python 2000 代表了 2000 年 5 月以来 CVS 树的状态,最重要的新Function是 Unicode 支持。当然,开发工作在 5 月之后 continue 进行,因此 1.6 树获得了一些修复,以确保它与 Python 2.0 向前兼容。因此 1.6 是 Python 演进的一部分,而不是侧分支。

因此,您应该对 Python 1.6 感兴趣吗?可能不是。 1.6final 和 2.0beta1 版本是在同一天(2000 年 9 月 5 日)发布的,计划是在一个月左右的时间内完成 Python 2.0 的发布。如果您有要维护的应用程序,那么pass将其升级到 1.6,对其进行修复,然后在一个月内pass升级到 2.0 进行另一轮破坏,似乎没有什么意义。您最好直接使用 2.0. 本文档中描述的大多数 true 有趣的Function仅在 2.0 中,因为在 5 月至 9 月之间完成了大量工作。

新的开发流程

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

现在,由 SourceForge 托管的最重要的服务是 Python CVS 树,它是受版本控制的存储库,其中包含 Python 的源代码。以前,大约有 7 个人具有对 CVS 树的写访问权限,并且所有补丁都必须由该简短名单上的人员之一进行检查和签入。显然,这不是很可扩展。pass将 CVS 树移到 SourceForge,可以向更多人授予写访问权限。截至 2000 年 9 月,有 27 个人能够签入更改,增加了四倍。这样就可以进行大规模的更改,如果必须pass一小组核心开发人员进行过滤,就不会try这些更改。例如,有一天,彼得·施耐德·坎普(Peter Schneider-Kamp)投入了脑袋,放弃了 K&R C 兼容性,并将 Python 的 C 源代码转换为 ANSIC。在获得 python-dev 邮件列表的批准后,他发起了一系列持续的签入工作大约一周后,其他开发人员也加入了帮助,这项工作已经完成。如果只有 5 个人具有写访问权,则该任务可能会被视为“不错,但不值得您花费时间和精力”,并且永远都做不到。

使用 SourceForge 服务的转变已导致开发速度显着提高。补丁现在可以由原始提交者以外的其他人提交,Comment,修订,并在人与人之间来回跳动,直到该补丁值得检查为止。在一个中央位置跟踪错误,可以将错误分配给特定人员进行修复,我们可以计算未解决错误的数量来衡量进度。这不是没有代价的:开发人员现在有更多的电子邮件可以处理,需要更多的邮件列表,并且必须为新环境编写特殊的工具。例如,SourceForge 发送了完全无用的默认补丁程序和错误通知电子邮件,因此 Ka-Ping Yee 编写了 HTML 屏幕抓取器来发送更多有用的消息。

易于添加代码引起了一些初期的成长难题,例如在代码准备就绪之前或未得到开发人员小组明确同意的情况下检入了代码。出现的批准过程与 Apache 组所使用的过程有些相似。开发人员可以在补丁程序中投票 1、0,-0 或-1; 1 和-1 表示接受或拒绝,而 0 和-0 表示显影剂对变化基本无动于衷,尽管有轻微的正斜率或负斜率。 Apache 模式最重要的变化是投票本质上是咨询性的,这使拥有终身仁慈独裁者地位的 Guido van Rossum 知道了普遍看法。即使社群不同意他的意见,他仍然可以无视投票结果,并批准或拒绝更改。

制作实际补丁是添加新Function的最后一步,并且与较早提出一个好的设计的任务相比,通常很容易。对新Function的讨论通常会充斥于冗长的邮件列表线程中,从而使讨论难以进行,而且没人能阅读到 python-dev 的每一篇文章。因此,已经构建了一个相对正式的流程来编写以 Internet RFC 流程为模型的 Python 增强建议(PEP)。 PEP 是描述拟议的新Function的文档草稿,并且会不断修订,直到社区接受或拒绝该提案为止。引用 PEP 1 的引言“ PEP 目的和指南”:

Note

PEP 代表 Python 增强提案。 PEP 是一个设计文档,向 Python 社区提供信息,或描述 Python 的新Function。 PEP 应提供该Function的简明技术规范以及该Function的原理。

我们打算将 PEP 用作主要机制,以提出新Function,收集社区对某个问题的意见以及记录已纳入 Python 的设计决策。 PEP 作者负责在社区内构建共识并记录不同意见。

阅读 PEP 1 的其余部分,以了解 PEP 编辑过程,样式和格式的详细信息。 PEP 虽然不属于 Python 2.0 发行版,但仍保留在 SourceForge 的 Python CVS 树中,并且也可以从https://www.python.org/dev/peps/以 HTML 形式获得。截至 2000 年 9 月,共有 25 个 PEPS,范围从 PEP 201,“锁步迭代”到 PEP 225,“逐元素/逐对象运算符”。

Unicode

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

Unicode 支持的finally接口是pass在 python-dev 邮件列表上进行的无休无止的讨论而得出的,该讨论主要由 Marc-AndréLemburg 实现,基于 Fredrik Lundh 的 Unicode 字符串类型实现。该接口的详细说明写为 PEP 100,“ Python Unicode 集成”。本文将仅介绍有关 Unicode 接口的最重要的要点。

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

Unicode 字符串与常规字符串一样,是不可变的序列类型。可以对它们进行索引和切片,但不能就地修改。 Unicode 字符串具有encode( [encoding] )方法,该方法以所需的编码返回 8 位字符串。编码由字符串命名,例如'ascii''utf-8''iso-8859-1'或其他名称。定义了编解码器 API,用于实现和注册新的编码,然后可在整个 Python 程序中使用这些编码。如果未指定编码,则默认编码通常为 7 位 ASCII,尽管可以pass在自定义版本site.py中调用sys.setdefaultencoding(encoding)函数在 Python 安装中更改默认编码。

使用默认的 ASCII 编码,将 8 位和 Unicode 字符串组合始终会强制转换为 Unicode。 'a' + u'bc'的结果是u'abc'

添加了新的内置Function,并修改了现有的内置Function以支持 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 字符串的内置函数,请报告为错误.)

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

codecs模块包含用于查找现有编码并注册新编码的Function。除非要实现新的编码,否则通常会使用codecs.lookup(encoding)函数,该函数返回一个 4 元素 Tuples:(encode_func, decode_func, stream_reader, stream_writer)

    • encode_func *是一个采用 Unicode 字符串并返回 2Tuples(string, length)的函数。 * string 是一个 8 位字符串,包含一部分(也许全部)转换为给定编码的 Unicode 字符串,而 length *则告诉您已转换了多少 Unicode 字符串。
    • decode_func encode_func 相反,它采用 8 位字符串并返回 2Tuples(ustring, length),该字符串由结果 Unicode 字符串 ustring 和整数 length *组成,该整数表示 8 位字符串的数量消耗。
    • stream_reader *是支持解码来自流的 Importing 的类。 * 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-8Importing:

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

可passre模块获得支持 Unicode 的正则表达式,该模块具有由 Secret Labs AB 的 Fredrik Lundh 编写的称为 SRE 的新基础实现。

添加了-U命令行选项,该选项使 Python 编译器将所有字符串 Literals 解释为 Unicode 字符串 Literals。它打算用于测试和将来对您的 Python 代码进行验证,因为某些将来的 Python 版本可能会放弃对 8 位字符串的支持,而仅提供 Unicode 字符串。

List Comprehensions

列表是 Python 中的主要数据类型,许多程序在某个时候操纵列表。列表上的两个常见操作是遍历它们,或者挑选出符合特定条件的元素,或者对每个元素应用某些Function。例如,给定一个字符串列表,您可能想要拉出包含给定子字符串的所有字符串,或者从每一行中删除尾随空格。

现有的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 ]

forin子句包含要迭代的序列。序列的长度不必相同,因为它们不是从左向右并行迭代的;在以下段落中将更清楚地解释这一点。生成的列表的元素将是* expression 的连续值。最后的if子句是可选的;如果存在,则仅当 condition 为 true 时才评估 expression *并将其添加到结果中。

为了使语义非常清楚,列表理解等效于以下 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.

这意味着当有多个forin子句时,结果列表将等于所有序列长度的乘积。如果您有两个长度为 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 的语法中引入歧义,如果* expression *正在创建一个 Tuples,则它必须用括号括起来。下面的第一个列表理解是语法错误,而第二个是正确的:

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

列表理解的概念最初来自Function编程语言 Haskell(https://www.haskell.org)。 Greg Ewing 最有效地 Arguments 了如何将它们添加到 Python,并编写了初始列表理解补丁,然后在 python-dev 邮件列表上讨论了似乎无休止的时间,并由 Skip Montanaro 保持最新状态。

Augmented Assignment

增强的赋值运算符是另一个长期要求的Function,已添加到 Python 2.0 中。增强的赋值运算符包括+=-=*=等。例如,语句a += 2将变量a的值增加 2,相当于稍长一些a = a + 2

受支持的赋值运算符的完整列表为+=-=*=/=%=**=&=|=^=>>=<<=。 Python 类可以pass定义名为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

到目前为止,字符串操作Function都位于string模块中,该模块通常是用 C 编写的strop模块的前端。Unicode 的添加给strop模块带来了困难,因为所有Function都需要按 Sequences 重写接受 8 位或 Unicode 字符串。对于带有 3 个字符串参数的string.replace()之类的函数,这意味着八个可能的排列以及相应的复杂代码。

相反,Python 2.0 将问题推到了字符串类型上,从而pass 8 位字符串和 Unicode 字符串上的方法提供了字符串操纵Function。

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

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

为了向后兼容,旧的string模块仍然存在,但是它主要充当新字符串方法的前端。

尽管在 JPython 中确实存在很长一段时间,但在 2.0 之前的版本中没有并行的两种方法是startswith()endswith()s.startswith(t)等效于s[:len(t)] == t,而s.endswith(t)等效于s[-len(t):] == t

值得特别一提的另一种方法是join()。字符串的join()方法接收一个参数,字符串序列,并且等效于旧string模块中的string.join()函数,但参数相反。换句话说,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属性中的引用仍然存在。但是该实例不再可以pass Python 代码访问,并且可以删除。如果多个对象相互引用,则它们可能会参与循环,从而导致所有对象泄漏。

Python 2.0 pass定期执行周期检测算法来解决此问题,该算法查找无法访问的周期并删除涉及的对象。新的gc模块提供了执行垃圾收集,获取调试统计信息以及调整收集器参数的Function。

运行周期检测算法需要一些时间,因此会导致一些额外的开销。希望在我们从使用 2.0 获得了循环收集的经验之后,Python 2.1 能够pass精心调整来最大程度地减少开销。目前尚不清楚会损失多少性能,因为对其进行基准测试非常棘手,并且关键取决于程序创建和销毁对象的频率。如果在运行 configure 脚本时指定--without-cycle-gc开关,甚至无法承受极小的速度损失或怀疑周期收集是否有错误,则可以在编译 Python 时禁用周期检测。

好几个人解决了这个问题,并为解决方案做出了贡献。 Toby Kelsey 撰写了周期检测方法的早期实现。当前的算法是由 Eric Tiedemann 在访问 CNRI 时提出的,Guido van Rossum 和 Neil Schemenauer 编写了两种不同的实现,后来由 Neil 进行了集成。许多人在此过程中提出了建议。 python-dev 邮件列表的 2000 年 3 月归档文件包含了大多数相关讨论,尤其是在标题为“ Python 参考循环集合”和“再次完成”的主题中。

其他核心变更

对 Python 的语法和内置函数进行了各种小的更改。这些变化都没有非常深远的影响,但它们却很方便。

较小的语言更改

新的语法使调用带有参数 Tuples 和/或关键字参数字典的给定函数更加方便。在 Python 1.5 和更早版本中,您将使用apply()内置函数:apply(f, args, kw)调用函数f(),其参数为 Tuples* 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
    ...

现在,pass在print后面加上>> file可以使print语句的输出定向到类似文件的对象,类似于 Unix shell 中的重定向操作符。以前,您要么必须使用类似文件的对象的write()方法,而该方法缺乏print的便利性和简单性,或者您可以为sys.stdout分配一个新值,然后恢复旧值。为了将输出发送到标准错误,编写此代码要容易得多:

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

现在可以在导入模块时使用语法import module as namefrom module import name as othername重命名模块。该补丁由 Thomas Wouters 提交。

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

以前,无法实现覆盖 Python 内置的in运算符并实现自定义版本的类。如果序列* seq 中存在 obj ,则obj in seq返回 true; Python pass简单地try序列的每个索引来计算该值,直到找到 obj *或遇到IndexError为止。 Moshe Zadka 提供了一个补丁,该补丁添加了contains()魔术方法,用于为in提供自定义实现。另外,用 C 编写的新内置对象可以pass序列协议中的新插槽定义in对其的含义。

Python 的早期版本使用递归算法删除对象。深度嵌套的数据结构可能导致解释器填满 C 堆栈并崩溃。 Christian Tismer 重新编写了删除逻辑以解决此问题。在相关说明中,比较了无限递归和崩溃的递归对象;杰里米·海尔顿(Jeremy Hylton)将代码重写为不再崩溃,而是产生了有用的结果。例如,在此代码之后:

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

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

将 Python 移植到 Itanium 处理器上的 64 位 Windows 上的工作已经完成,主要由 ActiveState 的 Trent Mick 进行。 (令人困惑的是,在 Win64 上sys.platform仍然是'win32',因为为了便于移植,MS Visual C 在 Itanium 上将代码视为 32 位.)有关更多信息,请参见http://pythonce.sourceforge.net/的 Python CE 页面。

另一个新平台是 Darwin/MacOSX。最初的支持是在 Python 2.0 中。如果您指定“ configure –with-dyld –with-suffix = .x”,则动态加载有效。有关更多说明,请查阅 Python 源分发中的自述文件。

已经try减轻 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()

引入了两个新的 exceptionTabErrorIndentationError。它们都是SyntaxError的子类,当发现 Python 代码缩进不正确时会引发它们。

内置Function的更改

添加了一个新的内置zip(seq1, seq2, ...)zip()返回一个 Tuples 列表,其中每个 Tuples 包含每个参数序列中的第 i 个元素。 zip()map(None, seq1, seq2)之间的区别在于,如果序列的长度不相同,则map()None填充序列,而zip()则将返回的列表截断为最短参数序列的长度。

现在,当第一个参数是字符串时,int()long()函数接受一个可选的“ base”参数。 int('123', 10)返回 123,而int('123', 16)返回 291.int(123, 16)引发TypeError异常,消息为“无法使用显式基数转换非字符串”。

包含更详细的版本信息的新变量已添加到sys模块。 sys.version_info是 Tuples(major, minor, micro, level, serial),例如,在假设的 2.0.1beta1 中,sys.version_info将是(2, 0, 1, 'beta', 1)。 * level *是用于finally发行版的字符串,例如"alpha""beta""final"

字典有一个奇怪的新方法setdefault(key, default),其行为与现有的get()方法相似。但是,如果缺少键,则setdefault()会像get()一样返回* default 的值,并将其作为 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,可以pass运行新脚本Misc/find_recursionlimit.py找到给定平台的大致最大值。

移植到 2.0

新的 Python 版本努力与以前的版本兼容,并且记录非常好。但是,某些更改被认为足够有用,通常是因为它们更改了最初被误认为是错误的初始设计决策,因此始终不能避免破坏向后兼容性。本部分列出了 Python 2.0 中的更改,这些更改可能会导致旧的 Python 代码崩溃。

可能会破坏最多代码的更改正在收紧某些方法接受的参数。一些方法将采用多个参数并将其视为 Tuples,尤其是各种列表方法,例如append()insert()。在 Python 的早期版本中,如果L是列表,则L.append( 1,2 )将 Tuples(1,2)追加到列表中。在 Python 2.0 中,这会引发TypeError异常,并显示以下消息:'append 恰好需要 1 个参数; 2 个给定的”。解决方法是简单地添加额外的一组括号以将两个值都作为 Tuples 传递:L.append( (1,2) )

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

socket模块中的某些Function仍然可以这种方式保留。例如,socket.connect( ('hostname', 25) )()是正确的形式,传递表示 IP 地址的 Tuples,但是socket.connect( 'hostname', 25 )()也可以。 socket.connect_ex()socket.bind()同样随和。 2.0alpha1 加强了这些Function,但是由于文档实际上使用了错误的多参数形式,因此许多人编写了会因严格检查而break的代码。 GvR 拒绝了面对公众反应的更改,因此对于socket模块,该文档是固定的,并且多参数形式被简单地标记为已弃用; 在以后的 Python 版本中再次加强。

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

AttributeErrorNameError异常具有更友好的错误消息,其文本类似于'Spam' instance has no attribute 'eggs'name 'eggs' is not defined。以前,错误消息只是缺少属性名eggs,而为利用这一事实而编写的代码将在 2.0 中break。

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

所有最细微的长整数更改是,尽管repr()仍然包含长整数str(),但不再具有结尾的“ L”字符。 “ L”使许多想要打印长整数(看起来像常规整数)的人感到恼火,因为他们必须竭尽全力砍掉字符。这在 2.0 中不再是问题,但是执行str(longval)[:-1]并假定'L'在那的代码现在将丢失最后一位。

现在,使用浮点数repr()str()使用不同的格式化精度。 repr()使用 C 的sprintf()%.17g格式字符串,而str()像以前一样使用%.12g。结果是对于某些数字repr()偶尔会显示比str()更多的小数位。例如,数字 8.1 不能完全用二进制表示,因此repr(8.1)'8.0999999999999996',而 str(8.1)是'8.1'

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

Extending/Embedding Changes

其中的一些更改是隐藏的,只有对编写 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 模块的用户将很高兴发现已添加了钩子,以便isinstance()issubclass()现在支持 ExtensionClasses。这意味着您不再需要记住编写诸如if type(obj) == myExtensionClass之类的代码,而可以使用更自然的if isinstance(obj, myExtensionClass)

格雷格·斯坦(Greg Stein)清理并重组了Python/importdl.c文件,该文件是#ifdef 的大量文件,用于支持许多不同平台上的动态加载。 importdl.c现在很小,并且特定于平台的代码已移入一堆Python/dynload_*.c文件中。另一个清理:Include /目录中还有许多my*.h文件,其中包含各种可移植性黑客;它们已合并到一个文件Include/pyport.h中。

弗拉基米尔·马兰戈佐夫(Vladimir Marangozov)期待已久的 malloc 重组已完成,这使得 Python 解释器使用自定义分配器而不是 C 的标准malloc()变得容易。有关文档,请阅读Include/pymem.hInclude/objimpl.h中的 Comments。有关敲定该接口的冗长讨论,请参见 python.org 上“补丁”和“ python-dev”列表的 Web 存档。

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

Windows 上的线程支持也得到了增强。 Windows 支持仅在发生争用时才使用内核对象的线程锁。在没有争用的常见情况下,它们使用更简单的函数,速度快一个数量级。 NT 上的 Python 1.5.2 的线程版本的速度是非线程版本的两倍。随着 2.0 的变化,差异仅 10%。这些改进由 Yakov Markovitch 贡献。

Python 2.0 的源代码现在仅使用 ANSI C 原型,因此编译 Python 现在需要使用 ANSI C 编译器,并且无法再使用仅支持 K&R C 的编译器来完成。

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

添加了三个新的便捷Function,它们用于在模块初始化时将常量添加到模块的字典中:PyModule_AddObject()PyModule_AddIntConstant()PyModule_AddStringConstant()。这些函数中的每个函数都带有一个模块对象,一个以空字符结尾的 C 字符串(包含要添加的名称),以及第三个参数,用于将值分配给该名称。第三个参数分别是 Python 对象,C long 或 C 字符串。

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

Distutils:使模块易于安装

在 Python 2.0 之前,安装模块是一件乏味的工作-无法自动找出在何处安装 Python 或用于扩展模块的编译器选项。软件作者必须经历繁琐的编辑 Makefile 和配置文件的仪式,这些文件仅在 Unix 上才 true 起作用,而 Windows 和 MacOS 不受支持。 Python 用户面临着截然不同的安装说明,安装说明在不同的扩展包之间有所不同,这使得 ManagementPython 安装变得很麻烦。

由 Greg Ward 负责的用于分发 Util 的 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"])

Cextensions 可能是最复杂的情况。这是取自 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文件)的命令处于开发的各个阶段。

所有这些都在新手册* Distributing Python Modules *中进行了记录,该手册加入了 Python 基本文档集。

XML Modules

Python 1.5.2 包含了一个简单的 XML 解析器,其形式为xmllib模块,由 Sjoerd Mullender 贡献。从 1.5.2 发行版开始,用于处理 XML 的两个不同的接口变得很常见:SAX2(XML 的简单 API 的版本 2)提供了一个事件驱动的接口,该接口与xmllib有一些相似之处,而 DOM(文档对象模型)则提供了一个树基于接口的接口,将 XML 文档转换为可以遍历和修改的节点树。 Python 2.0 在xml软件包中包含一个 SAX2 接口和一个精简的 DOM 接口。在这里,我们将简要概述这些新接口。有关完整的详细信息,请查阅 Python 文档或源代码。 Python XML SIG 也正在研究改进的文档。

SAX2 Support

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 Support

文档对象模型是 XML 文档的基于树的表示形式。顶级Document实例是树的根,并且有一个单独的孩子,即顶级Element实例。该Element具有表示字符数据和任何子元素的子节点,这些子节点可能还具有自己的子元素,依此类推。使用 DOM,您可以随意遍历结果树,访问元素和属性值,插入和删除节点,并将树转换回 XML。

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

Python 随附的 DOM 实现位于xml.dom.minidom模块中。它是对 XML 名称空间的支持的 1 级 DOM 的轻量级实现。提供了parse()parseString()便捷函数来生成 DOM 树:

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

docDocument实例。像所有其他 DOM 类(例如ElementText)一样,DocumentNodeBase Class 的子类。因此,DOM 树中的所有节点都支持某些通用方法,例如toxml(),该方法返回一个字符串,其中包含该节点及其子节点的 XML 表示形式。每个类还具有自己的特殊方法。例如,ElementDocument实例具有一种查找具有给定标签名称的所有子元素的方法。continue 前面的两行示例:

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,并且可以pass删除,添加或删除节点来轻松修改其子元素:

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/sigs/current/xml-sig获得。 PyXML 发行版还使用了包名xml。如果您编写了使用 PyXML 的程序,则可能想知道其与 2.0 xml软件包的兼容性。

答案是 Python 2.0 的xml包与 PyXML 不兼容,但是可以pass安装最新版本的 PyXML 使其兼容。许多应用程序都可以使用 Python 2.0 附带的 XML 支持,但是更复杂的应用程序将需要安装完整的 PyXML 软件包。安装后,PyXML 版本 0.6.0 或更高版本将替换 Python 随附的xml软件包,它将是标准软件包的严格超集,并添加了许多附加Function。 PyXML 中的一些其他Function包括:

  • 4 DOM,FourThought,Inc.的完整 DOM 实现。

  • xmlproc 验证解析器,由 Lars Marius Garshol 编写。

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

Module changes

对 Python 广泛的标准库进行了许多改进和错误修正。一些受影响的模块包括readlineConfigParsercgicalendarposixreadlinexmllibaifcchunk, waverandomshelvenntplib。请查阅 CVS 日志以获取逐个补丁的详细信息。

Brian Gallew 为socket模块贡献了 OpenSSL 支持。 OpenSSL 是安全套接字层的实现,该加密对pass套接字发送的数据进行加密。编译 Python 时,您可以编辑Modules/Setup以包括 SSL 支持,这将为socket模块添加一个附加Function:socket.ssl(socket, keyfile, certfile),该函数接受一个套接字对象并返回一个 SSL 套接字。 httpliburllib模块也已更改为支持https:// URL,尽管没有人pass SSL 实现 FTP 或 SMTP。

httplib模块已由 Greg Stein 重写以支持 HTTP/1.1. 提供了与 1.5 版本的httplib的向后兼容性,尽管使用 HTTP/1.1 Function(例如流水线)将要求重写代码以使用一组不同的接口。

Tkinter模块现在支持 Tcl/Tk 版本 8.1、8.2 或 8.3,并且已不再支持较早的 7.x 版本。 Tkinter 模块现在支持在 Tk 小部件中显示 Unicode 字符串。另外,Fredrik Lundh 进行了优化,使create_linecreate_polygon之类的操作更快,尤其是在使用大量坐标时。

从 Oliver Andrich 的增强版本开始,对curses模块进行了很大的扩展,以提供 ncurses 和 SYSV curses 的许多其他Function,例如颜色,备用字符集支持,填充和鼠标支持。这意味着该模块不再与仅具有 BSD curses 的 os 兼容,但是目前似乎没有任何维护的 os 属于此类。

如前面对 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:此模块pass提供 GNU gettext 消息目录库的接口,为 Python 程序提供国际化(I18N)和本地化(L10N)支持。 (由 Barry Warsaw 集成,由 Martin vonLöwis,Peter Funk 和 James Henstridge 分别提供.)

  • linuxaudiodev:支持 Linux 上的/dev/audio设备,它是现有sunaudiodev模块的双胞胎。 (由 Peter Bosch 提供,Jeremy Hylton 进行了修复.)

  • mmap:Windows 和 Unix 上用于内存 Map 文件的接口。文件的内容可以直接 Map 到内存中,这时它的行为就像可变的字符串,因此可以读取和修改其内容。它们甚至可以传递给需要普通字符串的函数,例如re模块。 (由 Sam Rushing 提供,A.M.Kuchling 进行了一些扩展.)

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

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

  • tabnanny:用于检查 Python 源代码中模棱两可的缩进的模块/脚本。 (由蒂姆·彼得斯贡献.)

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

  • webbrowser:一个模块,提供独立于平台的方式来在特定 URL 上启动 Web 浏览器。对于每个平台,将按特定 Sequences try各种浏览器。用户可以pass设置* BROWSER *环境变量来更改启动哪个浏览器。 (最初受到 Eric S. Raymond 对urllib的补丁的启发,该补丁添加了类似的Function,但finally模块来自 Fred Drake 最初以Tools/idle/BrowserControl.py实现的代码,并由 Fred 改编为标准库。)

  • _winreg:Windows 注册表的接口。 _winreg是自 1995 年以来 PythonWin 的一部分函数的改编,但现在已添加到核心发行版中,并进行了增强以支持 Unicode。 _winreg由 Bill Tutt 和 Mark Hammond 撰写。

  • zipfile:用于读写 ZIP 格式 Files 的模块。这些是由 DOS/Windows 上的 PKZIP 或 Unix 上的 zip 生成的 Files,不要与 gzip **格式的文件(由gzip模块支持)混淆(由 James C 提供) 。Ahlstrom。)

  • imputil:与现有的ihooks模块相比,该模块提供了一种用于编写自定义导入钩子的更简单方法。 (由格雷格·斯坦(Greg Stein)实施,并在此过程中对 python-dev 进行了很多讨论。)

IDLE Improvements

IDLE 是使用 Tkinter 编写的官方 Python 跨平台 IDE。 Python 2.0 包含 IDLE 0.6,该 IDLE 添加了许多新Function和改进。部分 Lists:

  • UI 的改进和优化,尤其是在语法突出显示和自动缩进方面。

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

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

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

  • 现在,IDLE 有一个命令行,该命令行在很大程度上类似于香草 Python 解释器。

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

  • 现在可以将 IDLE 打包安装。

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

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

已删除和已弃用的模块

删除了一些模块,因为它们已过时,或者因为现在有更好的方法可以执行相同的操作。 stdwin模块不见了;它用于不再开发的独立于平台的窗口工具包。

许多模块已移至lib-old子目录:cmpcmpcachedircmpdumpfindgreppackmailpolyutilwhatsoundzmod。如果您有依赖于已移至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。