Python 2.1 中的新功能

作者:

A.M. Kuchling

引言

本文解释了 Python 2.1 中的新功能。尽管 2.1 中的变化不如 Python 2.0 多,但仍然有一些令人惊喜之处。2.1 是第一个通过 Python 增强提案(即 PEP)指导发布的版本,因此大多数重大变化都有相应的 PEP 提供更完整的文档和设计原理。本文不试图完整地记录新功能,而只是为 Python 程序员提供新功能的概述。有关任何您特别感兴趣的新功能的更多详细信息,请参阅 Python 2.1 文档或特定的 PEP。

Python 开发团队最近的一个目标是加快新版本的发布速度,每 6 到 9 个月发布一个新版本。2.1 是第一个以这种更快的速度发布的版本,第一个 alpha 版本于 1 月份发布,距离 2.0 最终版本发布仅 3 个月。

Python 2.1 的最终版本于 2001 年 4 月 17 日发布。

PEP 227:嵌套作用域

Python 2.1 中最大的变化是 Python 的作用域规则。在 Python 2.0 中,在任何给定时间,最多有三个命名空间用于查找变量名:局部命名空间、模块级命名空间和内置命名空间。这常常让人们感到惊讶,因为它不符合他们的直观预期。例如,嵌套的递归函数定义不起作用

def f():
    ...
    def g(value):
        ...
        return g(value-1) + 1
    ...

函数 g() 总是会引发 NameError 异常,因为名称 g 的绑定既不在其局部命名空间中,也不在模块级命名空间中。这在实践中并不是一个大问题(您多久会像这样递归定义内部函数?),但这也使得使用 lambda 表达式变得笨拙,这在实践中是一个问题。在使用 lambda 的代码中,您经常会发现通过将局部变量作为参数的默认值来复制它们。

def find(self, name):
    "Return list of any entries equal to 'name'"
    L = filter(lambda x, name=name: x == name,
               self.list_attribute)
    return L

以强函数式风格编写的 Python 代码的可读性因此大受影响。

Python 2.1 最重要的变化是语言中添加了静态作用域来解决这个问题。首先,在上面的示例中,name=name 默认参数现在是不必要的。简单来说,当一个给定的变量名在一个函数中没有被赋值(通过赋值,或者 defclassimport 语句),对该变量的引用将在其 enclosing 作用域的局部命名空间中查找。关于规则的更详细解释以及实现的剖析,可以在 PEP 中找到。

此更改可能会导致某些兼容性问题,如果代码中在模块级别和包含其他函数定义的函数内部都使用了相同的变量名。但这似乎不太可能,因为这样的代码一开始就很难阅读。

此更改的一个副作用是,在某些条件下,from module import *exec 语句在函数作用域内是非法的。Python 参考手册一直指出 from module import * 仅在模块的顶层合法,但 CPython 解释器以前从未强制执行过此规定。作为嵌套作用域实现的一部分,将 Python 源代码转换为字节码的编译器必须生成不同的代码来访问包含作用域中的变量。from module import *exec 使编译器无法弄清这一点,因为它们向局部命名空间添加了在编译时不可知的名称。因此,如果一个函数包含函数定义或带有自由变量的 lambda 表达式,编译器将通过引发 SyntaxError 异常来标记此问题。

为了使前面的解释更清晰,这里有一个例子

x = 1
def f():
    # The next line is a syntax error
    exec 'x=2'
    def g():
        return x

包含 exec 语句的第 4 行是一个语法错误,因为 exec 会定义一个名为 x 的新局部变量,其值应由 g() 访问。

这不应该是一个很大的限制,因为 exec 在大多数 Python 代码中很少使用(而且当它被使用时,通常也表明设计不佳)。

出于兼容性考虑,嵌套作用域是逐步引入的;在 Python 2.1 中,它们默认未启用,但可以通过使用 PEP 236 中描述的 future 语句在模块内打开。(有关 PEP 236 的进一步讨论,请参阅下一节。)在 Python 2.2 中,嵌套作用域将成为默认设置,并且无法将其关闭,但用户将有 2.1 的整个生命周期来修复因其引入而导致的任何问题。

参见

PEP 227 - 静态嵌套作用域

由 Jeremy Hylton 编写并实现。

PEP 236: __future__ 指令

对于嵌套作用域的反应是普遍担忧 2.1 版本会破坏代码,这种担忧足够强烈,促使 Python 开发者采取更保守的方法。这种方法包括引入一个约定,用于在版本 N 中启用可选功能,该功能将在版本 N+1 中成为强制性功能。

语法使用 from...import 语句,其中使用保留的模块名 __future__。可以通过以下语句启用嵌套作用域

from __future__ import nested_scopes

虽然它看起来像一个普通的 import 语句,但它不是;对于这种 future 语句的放置位置有严格的规定。它们只能放在模块的顶部,并且必须在任何 Python 代码或常规 import 语句之前。这是因为这些语句会影响 Python 字节码编译器如何解析代码和生成字节码,因此它们必须在任何会导致生成字节码的语句之前。

参见

PEP 236 - 回到 __future__

由 Tim Peters 撰写,主要由 Jeremy Hylton 实现。

PEP 207: 丰富的比较

在早期版本中,Python 对用户定义类和扩展类型实现比较的支持相当简单。类可以实现一个 __cmp__() 方法,该方法接收类的两个实例,并且只能在它们相等时返回 0,否则返回 +1 或 -1;该方法不能引发异常或返回除布尔值之外的任何内容。Numeric Python 的用户常常觉得这种模型过于弱和限制性,因为在 Numeric Python 用于的数字处理程序中,能够对两个矩阵进行逐元素比较,返回一个包含每个元素的给定比较结果的矩阵会更有用。如果两个矩阵的大小不同,那么比较必须能够引发异常来表示错误。

在 Python 2.1 中,添加了丰富的比较以支持此需求。Python 类现在可以单独重载 <<=>>===!= 操作。新的魔术方法名称是

操作

方法名称

<

__lt__()

<=

__le__()

>

__gt__()

>=

__ge__()

==

__eq__()

!=

__ne__()

(这些魔术方法以相应的 Fortran 运算符 .LT..LE. 等命名。数值程序员几乎肯定对这些名称非常熟悉,并且会觉得它们容易记住。)

这些魔术方法中的每一个都采用 method(self, other) 的形式,其中 self 将是运算符左侧的对象,而 other 将是右侧的对象。例如,表达式 A < B 将导致调用 A.__lt__(B)

这些魔术方法中的每一个都可以返回任何值:布尔值、矩阵、列表或任何其他 Python 对象。或者,如果比较不可能、不一致或没有意义,它们也可以引发异常。

内置的 cmp(A,B) 函数可以使用丰富的比较机制,现在接受一个可选参数来指定要使用的比较操作;该参数是字符串 "<""<="">"">=""==""!=" 之一。如果调用时没有可选的第三个参数,cmp() 将像以前的 Python 版本一样只返回 -1、0 或 +1;否则它将调用适当的方法并可以返回任何 Python 对象。

对于 C 程序员来说,还有一些相应的有趣变化;类型对象中有一个新的插槽 tp_richcmp 和一个用于执行给定丰富比较的 API。我不会在这里介绍 C API,但会参考您查看 PEP 207 或 2.1 的 C API 文档,以获取相关函数的完整列表。

参见

PEP 207 - 丰富的比较

由 Guido van Rossum 撰写,主要基于 David Ascher 早期工作,并由 Guido van Rossum 实现。

PEP 230: 警告框架

在 Python 存在的 10 年里,它积累了一定数量的过时模块和功能。很难知道何时可以安全地删除某个功能,因为无法知道有多少代码使用它——也许没有程序依赖该功能,也许有很多。为了以更结构化的方式删除旧功能,添加了一个警告框架。当 Python 开发人员想要删除某个功能时,它首先会在下一个 Python 版本中触发一个警告。随后的 Python 版本就可以删除该功能,用户将有整整一个发布周期来移除对旧功能的使用。

Python 2.1 添加了用于此方案的警告框架。它添加了一个 warnings 模块,该模块提供用于发出警告和过滤掉不需要显示的警告的函数。第三方模块也可以使用此框架来弃用他们不再希望支持的旧功能。

例如,在 Python 2.1 中,regex 模块已被弃用,因此导入它会导致打印警告

>>> import regex
__main__:1: DeprecationWarning: the regex module
         is deprecated; please use the re module
>>>

可以通过调用 warnings.warn() 函数来发出警告

warnings.warn("feature X no longer supported")

第一个参数是警告消息;附加的可选参数可用于指定特定的警告类别。

可以添加过滤器以禁用某些警告;正则表达式模式可以应用于消息或模块名称以抑制警告。例如,您可能有一个使用 regex 模块的程序,并且现在不想花时间将其转换为使用 re 模块。可以通过调用以下函数来抑制警告

import warnings
warnings.filterwarnings(action = 'ignore',
                        message='.*regex module is deprecated',
                        category=DeprecationWarning,
                        module = '__main__')

这会添加一个过滤器,该过滤器仅适用于在 __main__ 模块中触发的 DeprecationWarning 类警告,并应用正则表达式仅匹配有关 regex 模块已弃用的消息,并将导致此类警告被忽略。警告也可以只打印一次,每次执行有问题的代码时打印,或者转换为异常,导致程序停止(当然,除非异常以通常方式捕获)。

Python 的 C API 中也添加了用于发出警告的函数;有关详细信息,请参阅 PEP 230 或 Python 的 API 文档。

参见

PEP 5 - 语言演进指南

由 Paul Prescod 撰写,旨在规定从 Python 中删除旧功能时应遵循的程序。本 PEP 中描述的政策尚未正式采用,但最终政策可能与 Prescod 的提案不会有太大差异。

PEP 230 - 警告框架

由 Guido van Rossum 撰写并实现。

PEP 229: 新的构建系统

在编译 Python 时,用户必须编辑 Modules/Setup 文件以启用各种附加模块;默认集相对较小,并且仅限于在大多数 Unix 平台上编译的模块。这意味着在具有更多功能的 Unix 平台(最值得注意的是 Linux)上,Python 安装通常不包含所有可能有的有用模块。

Python 2.0 添加了 Distutils,这是一组用于分发和安装扩展的模块。在 Python 2.1 中,Distutils 用于编译大部分标准库扩展模块,自动检测当前机器上支持哪些模块。希望这将使 Python 安装更容易且功能更丰富。

不再需要编辑 Modules/Setup 文件来启用模块,而是在 Python 源代码分发顶级目录中的 setup.py 脚本在构建时运行,并通过检查系统上的模块和头文件来尝试发现哪些模块可以启用。如果模块在 Modules/Setup 中配置,则 setup.py 脚本将不会尝试编译该模块,并将遵循 Modules/Setup 文件的内容。这提供了一种指定特定平台所需的任何奇怪的命令行标志或库的方法。

在构建机制的另一个影响深远的改变中,Neil Schemenauer 重构了事情,因此 Python 现在使用一个非递归的单一 makefile,而不是在顶级目录以及 Python/Parser/Objects/Modules/ 各个子目录中都有 makefile。这使得构建 Python 更快,也使得修改 Makefiles 更清晰、更简单。

参见

PEP 229 - 使用 Distutils 构建 Python

由 A.M. Kuchling 撰写并实现。

PEP 205:弱引用

弱引用,通过 weakref 模块提供,是 Python 程序员工具箱中一个虽小但有用的新数据类型。

存储对对象的引用(例如,在字典或列表中)的副作用是使该对象永远保持活动状态。在一些特定情况下,这种行为是不受欢迎的,对象缓存是最常见的一种,另一种是树等数据结构中的循环引用。

例如,考虑一个记忆化函数,它通过在字典中存储函数的参数及其结果来缓存另一个函数 f(x) 的结果

_cache = {}
def memoize(x):
    if _cache.has_key(x):
        return _cache[x]

    retval = f(x)

    # Cache the returned object
    _cache[x] = retval

    return retval

此版本适用于整数等简单事物,但它有一个副作用;_cache 字典持有对返回值的引用,因此在 Python 进程退出并清理之前,它们永远不会被解除分配。对于整数来说,这不是很明显,但如果 f() 返回一个对象或一个占用大量内存的数据结构,这可能是一个问题。

弱引用提供了一种实现缓存的方法,它不会使对象在超出其生命周期后仍然存在。如果一个对象只能通过弱引用访问,那么该对象将被解除分配,并且弱引用现在将指示它引用的对象不再存在。通过调用 wr = weakref.ref(obj) 来创建一个对对象 obj 的弱引用。通过像调用函数一样调用弱引用来返回所引用的对象:wr()。它将返回引用的对象,如果对象不再存在则返回 None

这使得编写一个 memoize() 函数成为可能,其缓存不会通过在缓存中存储弱引用来使对象保持活动状态。

_cache = {}
def memoize(x):
    if _cache.has_key(x):
        obj = _cache[x]()
        # If weak reference object still exists,
        # return it
        if obj is not None: return obj

    retval = f(x)

    # Cache a weak reference
    _cache[x] = weakref.ref(retval)

    return retval

weakref 模块还允许创建代理对象,这些对象表现得像弱引用——仅由代理对象引用的对象会被解除分配——但它们不需要显式调用来检索对象,而是只要对象仍然存在,代理就会透明地将所有操作转发给对象。如果对象被解除分配,尝试使用代理将引发 weakref.ReferenceError 异常。

proxy = weakref.proxy(obj)
proxy.attr   # Equivalent to obj.attr
proxy.meth() # Equivalent to obj.meth()
del obj
proxy.attr   # raises weakref.ReferenceError

参见

PEP 205 - 弱引用

由 Fred L. Drake, Jr. 撰写并实现。

PEP 232:函数属性

在 Python 2.1 中,函数现在可以附加任意信息。人们经常使用文档字符串来保存有关函数和方法的信息,因为 __doc__ 属性是唯一可以将任何信息附加到函数的方法。例如,在 Zope Web 应用程序服务器中,函数通过拥有文档字符串标记为可供公共访问,而在 John Aycock 的 SPARK 解析框架中,文档字符串保存要解析的 BNF 语法部分。这种重载是不幸的,因为文档字符串实际上旨在保存函数的文档;例如,这意味着您无法正确地记录 Zope 中用于私有用途的函数。

现在可以使用常规 Python 语法在函数上设置和检索任意属性

def f(): pass

f.publish = 1
f.secure = 1
f.grammar = "A ::= B (C D)*"

包含属性的字典可以作为函数的 __dict__ 进行访问。与类实例的 __dict__ 属性不同,在函数中,您可以实际将一个新字典赋值给 __dict__,尽管新值仅限于常规 Python 字典;您不能玩花招,将其设置为 UserDict 实例,或任何其他行为像映射的随机对象。

参见

PEP 232 - 函数属性

由 Barry Warsaw 撰写并实现。

PEP 235:在不区分大小写的平台上导入模块

有些操作系统的文件系统不区分大小写,MacOS 和 Windows 是主要的例子;在这些系统上,无法区分文件名 FILE.PYfile.py,即使它们确实以原始大小写存储文件名(它们也保留大小写)。

在 Python 2.1 中,import 语句将用于在不区分大小写的平台上模拟区分大小写。Python 现在默认会搜索第一个区分大小写的匹配项,如果没有找到此类文件则会引发 ImportError,因此 import file 不会导入名为 FILE.PY 的模块。可以通过在启动 Python 解释器之前设置 PYTHONCASEOK 环境变量来请求不区分大小写的匹配。

PEP 217:交互式显示钩子

在交互式使用 Python 解释器时,命令的输出会使用内置的 repr() 函数显示。在 Python 2.1 中,变量 sys.displayhook() 可以设置为一个可调用对象,该对象将替代 repr() 被调用。例如,您可以将其设置为一个特殊的漂亮打印函数

>>> # Create a recursive data structure
... L = [1,2,3]
>>> L.append(L)
>>> L # Show Python's default output
[1, 2, 3, [...]]
>>> # Use pprint.pprint() as the display function
... import sys, pprint
>>> sys.displayhook = pprint.pprint
>>> L
[1, 2, 3,  <Recursion on list with id=135143996>]
>>>

参见

PEP 217 - 交互式使用的显示钩子

由 Moshe Zadka 撰写并实现。

PEP 208:新的强制转换模型

C 语言级别的数值强制转换方式被显著修改。这将仅影响 Python C 扩展的作者,允许他们在编写支持数值运算的扩展类型时拥有更大的灵活性。

扩展类型现在可以在其 PyTypeObject 结构中设置类型标志 Py_TPFLAGS_CHECKTYPES,以表明它们支持新的强制转换模型。在此类扩展类型中,数值槽函数不能再假定它们将被传递两个相同类型的参数;相反,它们可能会被传递两个不同类型的参数,然后可以执行自己的内部强制转换。如果槽函数被传递了一个它无法处理的类型,它可以通过返回对 Py_NotImplemented 单例值的引用来指示失败。然后将尝试另一种类型的数值函数,也许它们可以处理该操作;如果另一种类型也返回 Py_NotImplemented,则将引发 TypeError。用 Python 编写的数值方法也可以返回 Py_NotImplemented,导致解释器表现得好像该方法不存在一样(也许会引发 TypeError,也许会尝试另一个对象的数值方法)。

参见

PEP 208 - 重构强制转换模型

由 Neil Schemenauer 撰写并实现,主要基于 Marc-André Lemburg 早期工作。阅读此文可了解 C 级别如何处理数值运算的细节。

PEP 241:Python 包中的元数据

Python 用户普遍抱怨没有一个所有现有 Python 模块的统一目录。T. Middleton 在 www.vex.net/parnassus/ 的 Vaults of Parnassus(于 2009 年 2 月停用,可在 Internet Archive Wayback Machine 中找到)是 Python 模块最大的目录,但在 Vaults 注册软件是可选的,许多人都不屑一顾。

作为解决此问题的第一步,使用 Distutils sdist 命令打包的 Python 软件将包含一个名为 PKG-INFO 的文件,其中包含有关包的信息,例如其名称、版本和作者(在编目术语中称为元数据)。PEP 241 包含了 PKG-INFO 文件中可以存在的所有字段。随着人们开始使用 Python 2.1 打包他们的软件,越来越多的包将包含元数据,从而可以构建自动化编目系统并对其进行实验。通过积累经验,或许可以设计一个真正好的目录,然后将其支持内置到 Python 2.2 中。例如,Distutils 的 sdistbdist_* 命令可以支持 upload 选项,该选项将自动将您的包上传到目录服务器。

即使您不使用 Python 2.1,也可以开始创建包含 PKG-INFO 的包,因为 Distutils 将为早期 Python 版本的用户发布新版本。Distutils 的 1.0.2 版本包括 PEP 241 中描述的更改,以及各种错误修复和增强功能。它将从 Distutils SIG 获得,地址为 https://pythonlang.cn/community/sigs/current/distutils-sig/

参见

PEP 241 - Python 软件包元数据

由 A.M. Kuchling 撰写并实现。

PEP 243 - 模块仓库上传机制

由 Sean Reifschneider 撰写,这份草案 PEP 描述了将 Python 包上传到中央服务器的提议机制。

新增和改进的模块

  • Ka-Ping Yee 贡献了两个新模块:inspect.py,一个用于获取实时 Python 代码信息的模块,以及 pydoc.py,一个用于交互式地将文档字符串转换为 HTML 或文本的模块。此外,现在会自动安装 Tools/scripts/pydoc,它使用 pydoc.py 根据 Python 模块、包或类名显示文档。例如,pydoc xml.dom 显示如下

    Python Library Documentation: package xml.dom in xml
    
    NAME
        xml.dom - W3C Document Object Model implementation for Python.
    
    FILE
        /usr/local/lib/python2.1/xml/dom/__init__.pyc
    
    DESCRIPTION
        The Python mapping of the Document Object Model is documented in the
        Python Library Reference in the section on the xml.dom package.
    
        This package contains the following modules:
          ...
    

    pydoc 还包含一个基于 Tk 的交互式帮助浏览器。pydoc 很快就会让人上瘾;快试试吧!

  • 标准库中添加了两个用于单元测试的不同模块。doctest 模块由 Tim Peters 贡献,提供了一个基于运行文档字符串中嵌入示例并将结果与预期输出进行比较的测试框架。PyUnit 由 Steve Purcell 贡献,是一个受 JUnit 启发的单元测试框架,而 JUnit 又改编自 Kent Beck 的 Smalltalk 测试框架。有关 PyUnit 的更多信息,请参阅 https://pyunit.sourceforge.net/

  • difflib 模块包含一个类 SequenceMatcher,它比较两个序列并计算将一个序列转换为另一个序列所需的更改。例如,此模块可用于编写类似于 Unix diff 程序的工具,实际上示例程序 Tools/scripts/ndiff.py 展示了如何编写此类脚本。

  • Thomas Gellekum 贡献了 curses.panel,它是面板库(ncurses 和 SYSV curses 的一部分)的一个包装器。面板库提供了具有额外深度功能的窗口。窗口可以在深度排序中向上或向下移动,面板库会计算面板重叠的位置以及哪些部分是可见的。

  • PyXML 包自 Python 2.0 以来已经发布了几个版本,Python 2.1 包含了更新版本的 xml 包。一些值得注意的更改包括支持 Expat 1.2 及更高版本、Expat 解析器处理 Python 支持的任何编码的文件能力,以及 SAX、DOM 和 minidom 模块的各种错误修复。

  • Ping 还贡献了另一个用于处理未捕获异常的钩子。sys.excepthook() 可以设置为一个可调用对象。当异常未被任何 tryexcept 块捕获时,异常将传递给 sys.excepthook(),然后它可以做任何它想做的事情。在第九届 Python 大会上,Ping 展示了这个钩子的一种应用:打印一个扩展的追溯,它不仅列出了堆栈帧,还列出了每个帧的函数参数和局部变量。

  • 模块 time 中的各种函数,例如 asctime()localtime(),需要一个浮点参数,其中包含自纪元以来的秒数。这些函数最常见的用途是处理当前时间,因此浮点参数已变为可选;当未提供值时,将使用当前时间。例如,日志文件条目通常需要一个包含当前时间的字符串;在 Python 2.1 中,可以使用 time.asctime(),而不是以前所需的更长的 time.asctime(time.localtime(time.time()))

    此更改由 Thomas Wouters 提议并实现。

  • ftplib 模块现在默认为被动模式检索文件,因为被动模式更有可能在防火墙后面工作。此请求来自 Debian 错误跟踪系统,因为其他 Debian 包使用 ftplib 检索文件,然后无法在防火墙后面工作。认为这不太可能给任何人造成问题,因为 Netscape 默认为被动模式,很少有人抱怨,但如果被动模式不适合您的应用程序或网络设置,请在 FTP 对象上调用 set_pasv(0) 以禁用被动模式。

  • 由 Grant Edwards 贡献,socket 模块已添加对原始套接字访问的支持。

  • pstats 模块现在包含一个简单的交互式统计浏览器,用于显示 Python 程序的计时配置文件,当该模块作为脚本运行时调用。由 Eric S. Raymond 贡献。

  • 添加了一个新的与实现相关的函数 sys._getframe([depth]),用于从当前调用堆栈返回给定的帧对象。sys._getframe() 返回调用堆栈顶部的帧;如果提供了可选的整数参数 depth,则该函数返回堆栈顶部以下 depth 次调用的帧。例如,sys._getframe(1) 返回调用者的帧对象。

    此功能仅存在于 CPython 中,而不存在于 Jython 或 .NET 实现中。将其用于调试,并抵制将其放入生产代码的诱惑。

其他更改和修复

由于发布周期较短,Python 2.1 中的较小更改相对较少。通过搜索 CVS 更改日志,发现应用了 117 个补丁,修复了 136 个错误;这两个数字都可能被低估了。一些更值得注意的更改包括

  • 现在可以选择使用专门的对象分配器,它应该比系统 malloc() 更快,并且内存开销更小。该分配器使用 C 的 malloc() 函数获取大块内存池,然后从这些内存池中满足较小的内存请求。可以通过向 configure 脚本提供 --with-pymalloc 选项来启用它;有关实现细节,请参阅 Objects/obmalloc.c

    C 扩展模块的作者应该在启用对象分配器的情况下测试他们的代码,因为一些不正确的代码可能会崩溃,导致运行时核心转储。Python 的 C API 中有许多内存分配函数,以前只是 C 库的 malloc()free() 的别名,这意味着如果您不小心调用了不匹配的函数,错误可能不会被注意到。当启用对象分配器时,这些函数不再是 malloc()free() 的别名,并且调用错误的函数来释放内存会导致核心转储。例如,如果内存是使用 PyMem_New 分配的,则必须使用 PyMem_Del() 释放,而不是 free()。Python 附带的几个模块因此而受影响并不得不进行修复;毫无疑问,还有更多第三方模块会遇到同样的问题。

    对象分配器由 Vladimir Marangozov 贡献。

  • 面向行的文件 I/O 速度得到了提升,因为人们经常抱怨其速度不足,而且它经常被用作一个简单的基准。因此,文件对象的 readline() 方法已被重写以显著提高速度。提速的确切程度会因平台而异,具体取决于 C 库的 getc() 有多慢,但大约提高了 66%,在某些特定操作系统上可能会更快。Tim Peters 为此更改做了大量的基准测试和编码工作,其动机是 comp.lang.python 中的一次讨论。

    Jeff Epler 还贡献了一个新的模块和文件对象方法。新方法 xreadlines() 类似于现有的内置 xrange()xreadlines() 返回一个不透明的序列对象,该对象只支持迭代,每次迭代读取一行,但不像现有的 readlines() 方法那样将整个文件读入内存。您会这样使用它

    for line in sys.stdin.xreadlines():
        # ... do something for each line ...
        ...
    

    有关行 I/O 更改的更详细讨论,请参阅 2001 年 1 月 1 日至 15 日的 python-dev 摘要,网址为 https://mail.python.org/pipermail/python-dev/2001-January/

  • 字典中添加了一个新方法 popitem(),以实现对字典内容的破坏性迭代;对于大型字典,这可以更快,因为无需构造一个包含所有键或值的列表。D.popitem() 从字典 D 中移除一个随机的 (key, value) 对,并将其作为 2 元组返回。此功能主要由 Tim Peters 和 Guido van Rossum 实现,此前 Moshe Zadka 提出了建议和初步补丁。

  • 当使用 from module import * 时,模块现在可以控制哪些名称被导入,方法是定义一个包含要导入名称列表的 __all__ 属性。一个常见的抱怨是,如果模块导入其他模块(例如 sysstring),from module import * 会将它们添加到导入模块的命名空间。为了解决这个问题,只需在 __all__ 中列出公共名称即可

    # List public names
    __all__ = ['Database', 'open']
    

    Ben Wolfson 最初提出并实现了一个更严格的版本,但在 python-dev 讨论后,一个更弱的最终版本被签入。

  • 以前对字符串应用 repr() 时,会使用八进制转义符表示不可打印字符;例如,换行符是 '\012'。这是 Python C 祖先的遗留痕迹,但如今八进制几乎没有实际用途。Ka-Ping Yee 建议使用十六进制转义符代替八进制,并为相应的字符使用 \n\t\r 转义符,并实现了这种新的格式化。

  • Jeremy Hylton 完成的编译器重组带来了令人欣喜的副作用,现在在编译时检测到的语法错误可以引发包含文件名和错误行号的异常。

  • 导入其他模块的 C 扩展已更改为使用 PyImport_ImportModule(),这意味着它们将使用已安装的任何导入钩子。这也鼓励需要从 C 代码导入其他模块的第三方扩展这样做。

  • 感谢 Fredrik Lundh,Unicode 字符数据库的大小又缩小了 340K。

  • 贡献了一些新的移植版本:MacOS X(Steven Majewski 贡献)、Cygwin(Jason Tishler 贡献);RISCOS(Dietmar Schwertberger 贡献);Unixware 7(Billy G. Allie 贡献)。

还有一些常见的次要错误修复、次要内存泄漏、文档字符串编辑和其他调整,数量过多,不值得一一列出;如果您需要完整详情,请参阅 CVS 日志。

致谢

作者要感谢以下人员对本文各种草稿提出的建议:Graeme Cross、David Goodger、Jay Graves、Michael Hudson、Marc-André Lemburg、Fredrik Lundh、Neil Schemenauer、Thomas Wouters。