Python 2.5 新特性

作者:

A.M. Kuchling

本文介绍了 Python 2.5 中的新特性。Python 2.5 的最终版本计划于 2006 年 8 月发布;PEP 356 描述了计划的发布时间表。Python 2.5 于 2006 年 9 月 19 日发布。

Python 2.5 中的更改是语言和库改进的有趣组合。我认为,库的增强对 Python 的用户社区来说更为重要,因为添加了几个广泛有用的软件包。新模块包括用于 XML 处理的 ElementTree (xml.etree)、SQLite 数据库模块 (sqlite) 以及用于调用 C 函数的 ctypes 模块。

语言的更改具有中等意义。添加了一些令人愉快的新功能,但它们中的大多数不是您每天都会使用的功能。使用一种新颖的语法最终将条件表达式添加到语言中;请参阅 PEP 308:条件表达式 部分。新的 ‘with’ 语句将使编写清理代码更容易(PEP 343:‘with’ 语句 部分)。现在可以将值传递给生成器(PEP 342:新的生成器特性 部分)。导入现在可以显示为绝对导入或相对导入(PEP 328:绝对和相对导入 部分)。更好地处理了异常处理的一些极端情况(PEP 341:统一的 try/except/finally 部分)。所有这些改进都是值得的,但它们是对一个或另一个特定语言功能的改进;它们都不是对 Python 语义的广泛修改。

除了语言和库的添加外,还在整个源代码树中进行了其他改进和错误修复。搜索 SVN 更改日志发现,在 Python 2.4 和 2.5 之间应用了 353 个补丁并修复了 458 个错误。(这两个数字都可能被低估了。)

本文不试图成为新功能的完整规范;相反,使用有用的示例简要介绍更改。有关完整详细信息,您应始终参考 https://docs.pythonlang.cn 上的 Python 2.5 文档。如果您想了解完整的实现和设计原理,请参考特定新功能的 PEP。

欢迎对本文档提出意见、建议和错误报告;请将它们通过电子邮件发送给作者或在 Python 错误跟踪器中打开一个错误。

PEP 308:条件表达式

长期以来,人们一直要求一种编写条件表达式的方法,条件表达式是根据布尔值为 true 或 false 返回值 A 或值 B 的表达式。条件表达式允许您编写与以下代码具有相同效果的单个赋值语句

if condition:
    x = true_value
else:
    x = false_value

在 python-dev 和 comp.lang.python 上,关于语法的讨论没完没了。甚至进行了一次投票,发现大多数投票者希望以某种形式使用条件表达式,但没有一种语法能得到明显的多数票的支持。候选语法包括 C 的 cond ? true_v : false_vif cond then true_v else false_v 和其他 16 种变体。

Guido van Rossum 最终选择了一种令人惊讶的语法

x = true_value if condition else false_value

评估仍然像在现有的布尔表达式中一样是惰性的,因此评估的顺序会稍微跳跃。首先评估中间的condition表达式,并且仅当条件为真时才评估true_value表达式。类似地,仅当条件为假时才评估false_value表达式。

这种语法可能看起来很奇怪和倒退;为什么条件位于表达式的中间,而不是像 C 的 c ? x : y 那样位于前面?通过将新语法应用于标准库中的模块并查看生成的代码如何读取来检查该决定。在许多使用条件表达式的情况下,一个值似乎是“常见情况”,而一个值是“特殊情况”,仅在较少的情况下(当不满足条件时)使用。条件语法使这种模式更加明显

contents = ((doc + '\n') if doc else '')

我将上面的语句理解为“这里 contents 通常被赋值为 doc+'\n' 的值;有时 doc 为空,在这种特殊情况下,返回一个空字符串。”我怀疑我是否会在没有明确的常见和不常见情况的情况下经常使用条件表达式。

有人讨论过语言是否应该要求用括号括起条件表达式。最终决定在 Python 语言的语法中要求使用括号,但是作为一种风格,我认为您应该始终使用它们。考虑这两个语句

# First version -- no parens
level = 1 if logging else 0

# Second version -- with parens
level = (1 if logging else 0)

在第一个版本中,我认为读者的眼睛可能会将语句分为 ‘level = 1’、‘if logging’、‘else 0’,并认为该条件决定是否执行对 level 的赋值。我认为第二个版本的可读性更好,因为它清楚地表明始终执行赋值,并且是在两个值之间进行选择。

包含括号的另一个原因:列表推导式和 lambda 表达式的一些奇怪组合可能看起来像不正确的条件表达式。有关一些示例,请参阅 PEP 308。如果您在条件表达式周围加上括号,就不会遇到这种情况。

另请参阅

PEP 308 - 条件表达式

由 Guido van Rossum 和 Raymond D. Hettinger 编写的 PEP;由 Thomas Wouters 实现。

PEP 309:偏函数应用

functools 模块旨在包含用于函数式编程的工具。

此模块中的一个有用工具是 partial() 函数。对于以函数式风格编写的程序,有时您会想要构造现有函数的变体,其中填充了一些参数。考虑一个 Python 函数 f(a, b, c);您可以创建一个与 f(1, b, c) 等效的新函数 g(b, c)。这称为“偏函数应用”。

partial() 接受参数 (function, arg1, arg2, ... kwarg1=value1, kwarg2=value2)。生成的对象是可调用的,因此您只需调用它即可使用填充的参数调用 function

这是一个小而真实的示例

import functools

def log (message, subsystem):
    "Write the contents of 'message' to the specified subsystem."
    print '%s: %s' % (subsystem, message)
    ...

server_log = functools.partial(log, subsystem='server')
server_log('Unable to open socket')

这是另一个示例,来自使用 PyGTK 的程序。这里正在动态构建上下文相关的弹出菜单。为菜单选项提供的回调是 open_item() 方法的部分应用版本,其中已提供了第一个参数。

...
class Application:
    def open_item(self, path):
       ...
    def init (self):
        open_func = functools.partial(self.open_item, item_path)
        popup_menu.append( ("Open", open_func, 1) )

functools 模块中的另一个函数是 update_wrapper(wrapper, wrapped) 函数,该函数可帮助您编写行为良好的装饰器。update_wrapper() 将名称、模块和文档字符串属性复制到包装器函数,以便更容易理解包装函数内部的回溯。例如,您可以编写

def my_decorator(f):
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    functools.update_wrapper(wrapper, f)
    return wrapper

wraps() 是一个装饰器,可以在你自己的装饰器内部使用,用来复制被装饰函数的元信息。之前示例的另一种写法是:

def my_decorator(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    return wrapper

另请参阅

PEP 309 - 部分函数应用

此 PEP 由 Peter Harris 提出和编写;由 Hye-Shik Chang 和 Nick Coghlan 实现,并由 Raymond Hettinger 进行调整。

PEP 314:Python 软件包元数据 v1.1

Distutils 添加了一些简单的依赖项支持。setup() 函数现在具有 requiresprovidesobsoletes 关键字参数。当使用 sdist 命令构建源代码发行版时,依赖项信息将记录在 PKG-INFO 文件中。

另一个新的关键字参数是 download_url,应该设置为软件包源代码的 URL。这意味着现在可以查找软件包索引中的条目,确定软件包的依赖关系,并下载所需的软件包。

VERSION = '1.0'
setup(name='PyPackage',
      version=VERSION,
      requires=['numarray', 'zlib (>=1.1.4)'],
      obsoletes=['OldPackage']
      download_url=('http://www.example.com/pypackage/dist/pkg-%s.tar.gz'
                    % VERSION),
     )

https://pypi.ac.cn 的 Python 软件包索引中的另一个新增强功能是存储软件包的源代码和二进制存档。新的 upload Distutils 命令会将软件包上传到存储库。

在上传软件包之前,必须能够使用 sdist Distutils 命令构建发行版。一旦它工作,就可以运行 python setup.py upload 将软件包添加到 PyPI 存档。可以选择通过提供 --sign--identity 选项来使用 GPG 对软件包进行签名。

软件包上传由 Martin von Löwis 和 Richard Jones 实现。

另请参阅

PEP 314 - Python 软件包元数据 v1.1

此 PEP 由 A.M. Kuchling、Richard Jones 和 Fred Drake 提出和编写;由 Richard Jones 和 Fred Drake 实现。

PEP 328:绝对和相对导入

在 Python 2.4 中实现了 PEP 328 中较简单的部分:现在可以使用圆括号将从模块导入的名称括起来,使用 from ... import ... 语句,从而更容易导入许多不同的名称。

更复杂的部分已在 Python 2.5 中实现:可以指定导入模块使用绝对导入还是包相对导入。计划是在未来的 Python 版本中将绝对导入作为默认值。

假设您有如下的包目录

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py

这定义了一个名为 pkg 的包,其中包含 pkg.mainpkg.string 子模块。

考虑 main.py 模块中的代码。如果它执行语句 import string 会发生什么?在 Python 2.4 及更早的版本中,它会首先在包的目录中查找以执行相对导入,找到 pkg/string.py,将该文件的内容作为 pkg.string 模块导入,并且该模块绑定到 pkg.main 模块命名空间中的名称 string

如果 pkg.string 是您想要的,那没问题。但是,如果您想要 Python 的标准 string 模块怎么办?没有干净的方法来忽略 pkg.string 并查找标准模块;通常您必须查看 sys.modules 的内容,这稍微有些不干净。Holger Krekel 的 py.std 包提供了一种更简洁的方法来执行从标准库的导入,import py; py.std.string.join(),但该包并非在所有 Python 安装中都可用。

阅读依赖相对导入的代码也不太清楚,因为读者可能会对打算使用哪个模块感到困惑,string 还是 pkg.string。Python 用户很快就学会了不在其包的子模块的名称中重复标准库模块的名称,但是您无法防止您的子模块的名称被用于 Python 未来版本中添加的新模块。

在 Python 2.5 中,可以使用 from __future__ import absolute_import 指令将 import 的行为切换到绝对导入。这种绝对导入行为将在未来版本(可能是 Python 2.7)中成为默认值。一旦绝对导入成为默认值,import string 将始终找到标准库的版本。建议用户应尽可能开始使用绝对导入,因此最好开始在代码中编写 from pkg import string

通过在使用 from ... import 形式时在模块名称中添加前导句点,仍然可以进行相对导入。

# Import names from pkg.string
from .string import name1, name2
# Import pkg.string
from . import string

这导入相对于当前包的 string 模块,因此在 pkg.main 中,这将从 pkg.string 导入 *name1* 和 *name2*。其他前导句点执行从当前包的父级开始的相对导入。例如,A.B.C 模块中的代码可以执行

from . import D                 # Imports A.B.D
from .. import E                # Imports A.E
from ..F import G               # Imports A.F.G

前导句点不能与 import modname 形式的导入语句一起使用,只能与 from ... import 形式一起使用。

另请参阅

PEP 328 - 导入:多行和绝对/相对导入

此 PEP 由 Aahz 编写;由 Thomas Wouters 实现。

https://pylib.readthedocs.io/

Holger Krekel 的 py 库,其中包含 py.std 包。

PEP 338:将模块作为脚本执行

在 Python 2.4 中添加的用于将模块作为脚本执行的 -m 开关获得了一些额外的功能。现在,该开关不再在 Python 解释器内部的 C 代码中实现,而是使用新模块 runpy 中的实现。

runpy 模块实现了更复杂的导入机制,因此现在可以运行包中的模块,例如 pychecker.checker。该模块还支持替代导入机制,例如 zipimport 模块。这意味着您可以将 .zip 存档的路径添加到 sys.path,然后使用 -m 开关从存档执行代码。

另请参阅

PEP 338 - 将模块作为脚本执行

此 PEP 由 Nick Coghlan 编写和实现。

PEP 341:统一的 try/except/finally

在 Python 2.5 之前,try 语句有两种形式。您可以使用 finally 块来确保代码始终执行,或使用一个或多个 except 块来捕获特定的异常。您不能同时组合 except 块和 finally 块,因为为组合版本生成正确的字节码很复杂,并且不清楚组合语句的语义应该是什么。

Guido van Rossum 花了一些时间使用 Java,Java 确实支持组合 except 块和 finally 块的等效项,这澄清了该语句应该意味着什么。在 Python 2.5 中,您现在可以编写

try:
    block-1 ...
except Exception1:
    handler-1 ...
except Exception2:
    handler-2 ...
else:
    else-block
finally:
    final-block

执行 *block-1* 中的代码。如果代码引发异常,则测试各种 except 块:如果异常属于 Exception1 类,则执行 *handler-1*;否则,如果它属于 Exception2 类,则执行 *handler-2*,依此类推。如果没有引发异常,则执行 *else-block*。

无论之前发生了什么,一旦代码块完成并且任何引发的异常得到处理,final-block 都会执行一次。即使异常处理程序或 else-block 中出现错误并引发新异常,final-block 中的代码仍然会运行。

另请参阅

PEP 341 - 统一 try-except 和 try-finally

Georg Brandl 撰写的 PEP;Thomas Lee 实现。

PEP 342:新的生成器特性

Python 2.5 添加了一种将值传递生成器中的简单方法。正如在 Python 2.3 中引入的那样,生成器只产生输出;一旦调用生成器的代码来创建一个迭代器,就没有办法在恢复执行时向该函数传递任何新信息。有时,能够传入一些信息会很有用。对此的非正式解决方案包括让生成器的代码查看全局变量,然后更改全局变量的值,或者传入调用者随后修改的某个可变对象。

为了刷新你对基本生成器的记忆,这里有一个简单的例子

def counter (maximum):
    i = 0
    while i < maximum:
        yield i
        i += 1

当你调用 counter(10) 时,结果是一个迭代器,它返回从 0 到 9 的值。在遇到 yield 语句时,迭代器返回提供的值并暂停函数的执行,保留局部变量。在后续调用迭代器的 next() 方法时,执行会恢复,从 yield 语句之后开始。

在 Python 2.3 中,yield 是一个语句;它不返回任何值。在 2.5 中,yield 现在是一个表达式,返回一个可以赋值给变量或以其他方式操作的值

val = (yield i)

我建议你始终在 yield 表达式周围加上括号,当你对返回值进行操作时,如上面的示例所示。括号并非总是必要的,但始终添加它们比必须记住何时需要它们更容易。

(PEP 342 解释了确切的规则,即 yield-表达式必须始终用括号括起来,除非它出现在赋值右侧的顶级表达式中。这意味着你可以写 val = yield i,但当有操作时必须使用括号,如 val = (yield i) + 12。)

通过调用生成器的 send(value) 方法将值发送到生成器中。然后恢复生成器的代码,yield 表达式返回指定的 value。如果调用常规的 next() 方法,则 yield 返回 None

下面是前面的示例,修改后允许更改内部计数器的值。

def counter (maximum):
    i = 0
    while i < maximum:
        val = (yield i)
        # If value provided, change counter
        if val is not None:
            i = val
        else:
            i += 1

下面是一个更改计数器的示例

>>> it = counter(10)
>>> print it.next()
0
>>> print it.next()
1
>>> print it.send(8)
8
>>> print it.next()
9
>>> print it.next()
Traceback (most recent call last):
  File "t.py", line 15, in ?
    print it.next()
StopIteration

yield 通常会返回 None,所以你应始终检查这种情况。除非你确定 send() 方法将是用于恢复生成器函数的唯一方法,否则不要在其表达式中直接使用它的值。

除了 send() 之外,生成器上还有另外两个新方法

  • throw(type, value=None, traceback=None) 用于在生成器内部引发异常;该异常由生成器暂停执行的 yield 表达式引发。

  • close() 在生成器内部引发一个新的 GeneratorExit 异常以终止迭代。在收到此异常时,生成器的代码必须引发 GeneratorExitStopIteration。捕获 GeneratorExit 异常并返回值是非法的,将触发 RuntimeError;如果该函数引发其他异常,则该异常会传播到调用者。close() 还会在生成器被垃圾回收时由 Python 的垃圾回收器调用。

    如果你需要在发生 GeneratorExit 时运行清理代码,我建议使用 try: ... finally: 套件,而不是捕获 GeneratorExit

这些更改的累积效应是将生成器从单向信息生产者转变为生产者和消费者。

生成器也变成了协程,这是一种更广义的子例程形式。子例程在一个点进入,在另一个点退出(函数的顶部和 return 语句),但协程可以在许多不同的点进入、退出和恢复(yield 语句)。我们将不得不弄清楚在 Python 中有效使用协程的模式。

添加 close() 方法有一个不明显的副作用。当生成器被垃圾回收时会调用 close(),这意味着生成器的代码在生成器被销毁之前最后一次运行。这最后一次机会意味着现在可以保证生成器中的 try...finally 语句可以正常工作;finally 子句现在总是有机会运行。因此,你不能将 yield 语句与 try...finally 套件混合使用的语法限制已被删除。这似乎是语言方面的小知识点,但为了实现 PEP 343 描述的 with 语句,实际上必须使用生成器和 try...finally。我将在下一节中讨论这个新语句。

此更改的另一个更深奥的影响:以前,生成器的 gi_frame 属性始终是一个帧对象。现在,一旦生成器耗尽,gi_frame 就有可能为 None

另请参阅

PEP 342 - 通过增强生成器实现协程

Guido van Rossum 和 Phillip J. Eby 撰写的 PEP;由 Phillip J. Eby 实现。包括一些将生成器用作协程的更高级用法的示例。

这些功能的早期版本在 Raymond Hettinger 的 PEP 288 和 Samuele Pedroni 的 PEP 325 中提出。

https://en.wikipedia.org/wiki/Coroutine

维基百科关于协程的条目。

https://web.archive.org/web/20160321211320/http://www.sidhe.org/~dan/blog/archives/000178.html

Dan Sugalski 从 Perl 的角度对协程的解释。

PEP 343:'with' 语句

with’ 语句简化了以前使用 try...finally 代码块来确保执行清理代码的代码。在本节中,我将讨论该语句的常用方式。在下一节中,我将检查实现细节并展示如何编写用于此语句的对象。

with’ 语句是一种新的控制流结构,其基本结构为

with expression [as variable]:
    with-block

表达式会被求值,其结果应该是一个支持上下文管理协议的对象(即,具有 __enter__()__exit__() 方法)。

在执行 with-block 之前会调用对象的 __enter__() 方法,因此可以运行设置代码。它也可能返回一个值,该值绑定到名称 variable (如果给定)。(请注意,variable 不会 被赋予 expression 的结果。)

with-block 执行完毕后,会调用对象的 __exit__() 方法,即使代码块引发了异常,因此可以运行清理代码。

要在 Python 2.5 中启用该语句,您需要在模块中添加以下指令

from __future__ import with_statement

该语句在 Python 2.6 中将始终启用。

一些标准的 Python 对象现在支持上下文管理协议,并且可以与 ‘with’ 语句一起使用。文件对象就是一个例子

with open('/etc/passwd', 'r') as f:
    for line in f:
        print line
        ... more processing code ...

在此语句执行后,f 中的文件对象将自动关闭,即使 for 循环在代码块中途引发了异常。

注意

在这种情况下,f 是由 open() 创建的同一对象,因为 __enter__() 返回 self

threading 模块的锁和条件变量也支持 ‘with’ 语句

lock = threading.Lock()
with lock:
    # Critical section of code
    ...

在执行代码块之前获取锁,并且在代码块完成时始终释放锁。

decimal 模块中的新 localcontext() 函数可以轻松保存和恢复当前的十进制上下文,该上下文封装了计算所需的精度和舍入特性

from decimal import Decimal, Context, localcontext

# Displays with default precision of 28 digits
v = Decimal('578')
print v.sqrt()

with localcontext(Context(prec=16)):
    # All code in this block uses a precision of 16 digits.
    # The original context is restored on exiting the block.
    print v.sqrt()

编写上下文管理器

在底层,‘with’ 语句相当复杂。大多数人只会将 ‘with’ 与现有对象一起使用,不需要了解这些细节,因此如果您愿意,可以跳过本节的其余部分。新对象的作者将需要了解底层实现的细节,并应继续阅读。

上下文管理协议的高级解释是

  • 表达式会被求值,其结果应该是一个称为“上下文管理器”的对象。上下文管理器必须具有 __enter__()__exit__() 方法。

  • 调用上下文管理器的 __enter__() 方法。返回的值将赋值给 VAR。如果不存在 'as VAR' 子句,则该值将被简单地丢弃。

  • 执行 BLOCK 中的代码。

  • 如果 BLOCK 引发异常,则会使用异常详细信息(与 sys.exc_info() 返回的相同值)调用 __exit__(type, value, traceback) 方法。该方法的返回值控制是否重新引发异常:任何假值都会重新引发异常,而 True 将导致抑制异常。您很少需要抑制异常,因为如果您这样做,则包含 ‘with’ 语句的代码作者将永远不会意识到出现了问题。

  • 如果 BLOCK 没有引发异常,则仍会调用 __exit__() 方法,但 typevaluetraceback 都为 None

让我们考虑一个例子。我不会展示详细的代码,而只会勾勒出支持事务的数据库所必需的方法。

(对于不熟悉数据库术语的人:对数据库的一组更改被分组到一个事务中。事务可以被提交,这意味着所有更改都写入数据库,或者回滚,这意味着所有更改都被丢弃并且数据库保持不变。有关更多信息,请参阅任何数据库教科书。)

假设有一个表示数据库连接的对象。我们的目标是让用户编写如下代码

db_connection = DatabaseConnection()
with db_connection as cursor:
    cursor.execute('insert into ...')
    cursor.execute('delete from ...')
    # ... more operations ...

如果代码块中的代码运行完美,则应提交事务;如果存在异常,则应回滚事务。这是我将假设的 DatabaseConnection 的基本接口

class DatabaseConnection:
    # Database interface
    def cursor (self):
        "Returns a cursor object and starts a new transaction"
    def commit (self):
        "Commits current transaction"
    def rollback (self):
        "Rolls back current transaction"

__enter__() 方法非常简单,只需要启动一个新的事务即可。对于此应用程序,生成的游标对象将是一个有用的结果,因此该方法将返回它。然后,用户可以在其 ‘with’ 语句中添加 as cursor,以将游标绑定到变量名。

class DatabaseConnection:
    ...
    def __enter__ (self):
        # Code to start a new transaction
        cursor = self.cursor()
        return cursor

__exit__() 方法是最复杂的,因为大部分工作都必须在此处完成。该方法必须检查是否发生了异常。如果没有异常,则提交事务。如果存在异常,则回滚事务。

在下面的代码中,执行将直接落到函数的末尾,返回默认值 NoneNone 为假,因此将自动重新引发异常。如果您愿意,您可以更加明确地在标记的位置添加 return 语句。

class DatabaseConnection:
    ...
    def __exit__ (self, type, value, tb):
        if tb is None:
            # No exception, so commit
            self.commit()
        else:
            # Exception occurred, so rollback.
            self.rollback()
            # return False

contextlib 模块

新的 contextlib 模块提供了一些函数和一个装饰器,这些函数和装饰器对于编写与 ‘with’ 语句一起使用的对象非常有用。

该装饰器名为 contextmanager(),它允许您编写单个生成器函数,而不是定义新类。生成器应只产生一个值。直到 yield 的代码将作为 __enter__() 方法执行,并且生成的值将是该方法的返回值,该返回值将绑定到 ‘with’ 语句的 as 子句中的变量(如果存在)。yield 之后的代码将在 __exit__() 方法中执行。代码块中引发的任何异常都将由 yield 语句引发。

可以使用此装饰器编写上一节中的数据库示例,如下所示

from contextlib import contextmanager

@contextmanager
def db_transaction (connection):
    cursor = connection.cursor()
    try:
        yield cursor
    except:
        connection.rollback()
        raise
    else:
        connection.commit()

db = DatabaseConnection()
with db_transaction(db) as cursor:
    ...

contextlib 模块还具有一个 nested(mgr1, mgr2, ...) 函数,该函数组合了多个上下文管理器,因此您无需编写嵌套的 ‘with’ 语句。在此示例中,单个 ‘with’ 语句既启动了数据库事务,又获取了线程锁

lock = threading.Lock()
with nested (db_transaction(db), lock) as (cursor, locked):
    ...

最后,closing(object) 函数返回 object,以便将其绑定到变量,并在代码块末尾调用 object.close

import urllib, sys
from contextlib import closing

with closing(urllib.urlopen('http://www.yahoo.com')) as f:
    for line in f:
        sys.stdout.write(line)

另请参阅

PEP 343 - “with” 语句

由 Guido van Rossum 和 Nick Coghlan 编写的 PEP;由 Mike Bland、Guido van Rossum 和 Neal Norwitz 实现。该 PEP 显示了为 ‘with’ 语句生成的代码,这有助于了解该语句的工作原理。

关于 contextlib 模块的文档。

PEP 352:异常作为新式类

现在,异常类可以是新式类,而不仅仅是经典类,并且内置的 Exception 类以及所有标准的内置异常(NameErrorValueError 等)现在都是新式类。

异常的继承层次结构进行了一些调整。在 2.5 版本中,继承关系如下:

BaseException       # New in Python 2.5
|- KeyboardInterrupt
|- SystemExit
|- Exception
   |- (all other current built-in exceptions)

进行此调整是因为人们经常想要捕获所有指示程序错误的异常。KeyboardInterruptSystemExit 虽然不是错误,但通常表示明确的操作,例如用户按下 Control-C 或代码调用 sys.exit()。一个裸的 except: 将捕获所有异常,因此您通常需要列出 KeyboardInterruptSystemExit 以便重新引发它们。通常的模式是:

try:
    ...
except (KeyboardInterrupt, SystemExit):
    raise
except:
    # Log error...
    # Continue running program...

在 Python 2.5 中,您现在可以编写 except Exception 来实现相同的结果,捕获所有通常指示错误的异常,但保留 KeyboardInterruptSystemExit 不受影响。与之前的版本一样,裸的 except: 仍然捕获所有异常。

Python 3.0 的目标是要求任何作为异常引发的类都必须派生自 BaseExceptionBaseException 的某个后代,并且 Python 2.x 系列的未来版本可能会开始强制执行此约束。因此,我建议您现在开始使所有异常类都派生自 Exception。有人建议应该在 Python 3.0 中删除裸的 except: 形式,但 Guido van Rossum 尚未决定是否这样做。

在 Python 2.5 中,将字符串作为异常引发(如语句 raise "Error occurred" 中)已被弃用,并且会触发警告。目标是在几个版本后删除字符串异常功能。

另请参阅

PEP 352 - 异常所需的超类

由 Brett Cannon 和 Guido van Rossum 编写的 PEP;由 Brett Cannon 实现。

PEP 353:使用 ssize_t 作为索引类型

对 Python 的 C API 进行了广泛的更改,使用新的 Py_ssize_t 类型定义而不是 int,这将允许解释器在 64 位平台上处理更多数据。此更改不会影响 32 位平台上 Python 的容量。

Python 解释器的各个部分使用 C 的 int 类型来存储大小或计数;例如,列表或元组中的项目数存储在一个 int 中。大多数 64 位平台的 C 编译器仍然将 int 定义为 32 位类型,这意味着列表最多只能容纳 2**31 - 1 = 2147483647 个项目。(实际上,64 位 C 编译器可以使用一些不同的编程模型 - 请参阅 https://unix.org/version2/whatsnew/lp64_wp.html 进行讨论 - 但最常用的模型将 int 保留为 32 位。)

在 32 位平台上,2147483647 个项目的限制实际上并不重要,因为您会在达到长度限制之前耗尽内存。每个列表项都需要一个指针的空间(4 个字节),加上用于表示该项的 PyObject 的空间。2147483647*4 已经比 32 位地址空间可以包含的字节数更多。

但是,可以在 64 位平台上寻址那么多内存。该大小的列表的指针只需要 16 GiB 的空间,因此 Python 程序员构造如此大的列表并非不合理。因此,必须更改 Python 解释器以使用 int 以外的类型,这将是 64 位平台上使用的 64 位类型。此更改将在 64 位机器上导致不兼容,因此认为现在进行转换是值得的,此时 64 位用户的数量仍然相对较少。(在 5 年或 10 年后,我们可能会在 64 位机器上,那时转换会更加痛苦。)

此更改对 C 扩展模块的作者影响最大。Python 字符串和容器类型(如列表和元组)现在使用 Py_ssize_t 来存储其大小。诸如 PyList_Size() 之类的函数现在返回 Py_ssize_t。因此,扩展模块中的代码可能需要将某些变量更改为 Py_ssize_t

PyArg_ParseTuple()Py_BuildValue() 函数有一个新的转换代码 n,用于 Py_ssize_tPyArg_ParseTuple()s#t# 默认仍输出 int,但是您可以在包含 Python.h 之前定义宏 PY_SSIZE_T_CLEAN,使它们返回 Py_ssize_t

PEP 353 有一个关于转换指南的部分,扩展作者应该阅读以了解如何支持 64 位平台。

另请参阅

PEP 353 - 使用 ssize_t 作为索引类型

由 Martin von Löwis 编写和实现的 PEP。

PEP 357:’__index__’ 方法

NumPy 开发人员遇到了一个只能通过添加新的特殊方法 __index__() 来解决的问题。使用切片表示法(如 [start:stop:step] 中),startstopstep 索引的值都必须是整数或长整数。NumPy 定义了对应于 8、16、32 和 64 位无符号和有符号整数的各种专用整数类型,但没有办法表示这些类型可以用作切片索引。

切片不能只使用现有的 __int__() 方法,因为该方法也用于实现强制转换为整数。如果切片使用 __int__(),则浮点数也将成为合法的切片索引,这显然是不希望的行为。

相反,添加了一个名为 __index__() 的新特殊方法。它不接受任何参数,并返回一个整数,该整数给出要使用的切片索引。例如:

class C:
    def __index__ (self):
        return self.value

返回值必须是 Python 整数或长整数。解释器将检查返回的类型是否正确,如果未满足此要求,则会引发 TypeError

在 C 级别的 PyNumberMethods 结构中添加了相应的 nb_index 插槽,以便 C 扩展实现此协议。PyNumber_Index(obj) 可在扩展代码中用于调用 __index__() 函数并检索其结果。

另请参阅

PEP 357 - 允许将任何对象用于切片

由 Travis Oliphant 编写和实现的 PEP。

其他语言更改

以下是 Python 2.5 对核心 Python 语言所做的所有更改。

  • dict 类型新增了一个钩子,允许子类在字典中不包含键时提供默认值。当找不到键时,将调用字典的 __missing__(key) 方法。此钩子用于实现 collections 模块中的新 defaultdict 类。以下示例定义了一个字典,对于任何缺失的键返回零。

    class zerodict (dict):
        def __missing__ (self, key):
            return 0
    
    d = zerodict({1:1, 2:2})
    print d[1], d[2]   # Prints 1, 2
    print d[3], d[4]   # Prints 0, 0
    
  • 8 位字符串和 Unicode 字符串都具有新的 partition(sep)rpartition(sep) 方法,从而简化了常见的使用场景。

    find(S) 方法通常用于获取索引,然后使用该索引来切片字符串,并获取分隔符之前和之后的部分。partition(sep) 将此模式浓缩为单个方法调用,该调用返回一个包含分隔符之前的子字符串、分隔符本身以及分隔符之后的子字符串的 3 元组。如果未找到分隔符,则元组的第一个元素是整个字符串,其他两个元素为空。rpartition(sep) 也返回一个 3 元组,但从字符串的末尾开始搜索;r 代表“反向”。

    一些例子

    >>> ('https://pythonlang.cn').partition('://')
    ('http', '://', 'www.python.org')
    >>> ('file:/usr/share/doc/index.html').partition('://')
    ('file:/usr/share/doc/index.html', '', '')
    >>> (u'Subject: a quick question').partition(':')
    (u'Subject', u':', u' a quick question')
    >>> 'www.python.org'.rpartition('.')
    ('www.python', '.', 'org')
    >>> 'www.python.org'.rpartition(':')
    ('', '', 'www.python.org')
    

    (由 Fredrik Lundh 根据 Raymond Hettinger 的建议实现。)

  • 字符串类型的 startswith()endswith() 方法现在接受字符串元组进行检查。

    def is_image_file (filename):
        return filename.endswith(('.gif', '.jpg', '.tiff'))
    

    (由 Georg Brandl 根据 Tom Lynn 的建议实现。)

  • min()max() 内置函数获得了 key 关键字参数,类似于 sort()key 参数。此参数提供一个函数,该函数接受单个参数并为列表中的每个值调用;min()/max() 将返回此函数返回的最小值/最大值的元素。例如,要查找列表中最长的字符串,您可以执行

    L = ['medium', 'longest', 'short']
    # Prints 'longest'
    print max(L, key=len)
    # Prints 'short', because lexicographically 'short' has the largest value
    print max(L)
    

    (由 Steven Bethard 和 Raymond Hettinger 贡献。)

  • 两个新的内置函数 any()all(),用于评估迭代器是否包含任何真值或假值。any() 如果迭代器返回的任何值为真,则返回 True;否则将返回 Falseall() 仅当迭代器返回的所有值都评估为真时才返回 True。(由 Guido van Rossum 建议,由 Raymond Hettinger 实现。)

  • 一个类的 __hash__() 方法的结果现在可以是长整数或常规整数。如果返回长整数,则会取该值的哈希值。在早期版本中,哈希值必须是常规整数,但在 2.5 中,id() 内置函数已更改为始终返回非负数,并且用户似乎经常在 __hash__() 方法中使用 id(self)(尽管不建议这样做)。

  • ASCII 现在是模块的默认编码。如果模块包含带有 8 位字符的字符串字面量,但没有编码声明,则现在是语法错误。在 Python 2.4 中,这会触发警告,而不是语法错误。有关如何声明模块编码的信息,请参阅 PEP 263;例如,您可以在源文件的顶部附近添加如下行

    # -*- coding: latin1 -*-
    
  • 当您尝试比较 Unicode 字符串和无法使用默认 ASCII 编码转换为 Unicode 的 8 位字符串时,会触发新的警告 UnicodeWarning。比较的结果为假

    >>> chr(128) == unichr(128)   # Can't convert chr(128) to Unicode
    __main__:1: UnicodeWarning: Unicode equal comparison failed
      to convert both arguments to Unicode - interpreting them
      as being unequal
    False
    >>> chr(127) == unichr(127)   # chr(127) can be converted
    True
    

    以前,这将引发 UnicodeDecodeError 异常,但在 2.5 中,当访问字典时,这可能会导致令人困惑的问题。如果您查找 unichr(128),并且 chr(128) 被用作键,则会得到 UnicodeDecodeError 异常。2.5 中的其他更改导致此异常被引发,而不是被实现字典的 dictobject.c 中的代码抑制。

    为此类比较引发异常是完全正确的,但此更改可能会破坏代码,因此引入了 UnicodeWarning

    (由 Marc-André Lemburg 实现。)

  • Python 程序员有时会犯的一个错误是忘记在包目录中包含 __init__.py 模块。调试此错误可能会令人困惑,并且通常需要使用 -v 开关运行 Python 以记录所有搜索的路径。在 Python 2.5 中,当导入会将目录识别为包但未找到 __init__.py 时,会触发新的 ImportWarning 警告。默认情况下,此警告会被静默忽略;在运行 Python 可执行文件时提供 -Wd 选项以显示警告消息。(由 Thomas Wouters 实现。)

  • 类定义中的基类列表现在可以为空。例如,这现在是合法的

    class C():
        pass
    

    (由 Brett Cannon 实现。)

交互式解释器更改

在交互式解释器中,quitexit 长期以来都是字符串,以便新用户在尝试退出时获得一些有用的消息

>>> quit
'Use Ctrl-D (i.e. EOF) to exit.'

在 Python 2.5 中,quitexit 现在是对象,它们仍然生成自身的字符串表示形式,但也是可调用的。尝试 quit()exit() 的新手现在将按照他们的预期退出解释器。(由 Georg Brandl 实现。)

Python 可执行文件现在接受标准的长选项 --help--version;在 Windows 上,它还接受 /? 选项以显示帮助消息。(由 Georg Brandl 实现。)

优化

其中一些优化是在 2006 年 5 月 21 日至 28 日在冰岛雷克雅未克举行的 NeedForSpeed 冲刺活动中开发的。冲刺活动专注于 CPython 实现的速度增强,由 EWT LLC 资助,并得到 CCP Games 的本地支持。在此次冲刺活动中添加的优化在以下列表中特别标记。

  • 当在 Python 2.4 中引入时,内置的 setfrozenset 类型是在 Python 的字典类型之上构建的。在 2.5 中,内部数据结构已针对实现集合进行了自定义,因此集合将使用三分之一的内存,并且速度会快一些。(由 Raymond Hettinger 实现。)

  • 一些 Unicode 操作的速度得到了提高,例如查找子字符串、字符串拆分以及字符映射编码和解码。(子字符串搜索和拆分改进是由 Fredrik Lundh 和 Andrew Dalke 在 NeedForSpeed 冲刺活动中添加的。字符映射由 Walter Dörwald 和 Martin von Löwis 改进。)

  • long(str, base) 函数现在在长数字字符串上更快,因为计算的中间结果更少。峰值出现在 800-1000 位左右的字符串,此时该函数的速度快了 6 倍。(由 Alan McIntyre 贡献,并在 NeedForSpeed 冲刺活动中提交。)

  • 现在,混合使用 for line in file 迭代文件和调用文件对象的 read()/readline()/readlines() 方法是非法的。迭代使用内部缓冲区,而 read*() 方法不使用该缓冲区。相反,它们会返回缓冲区之后的数据,导致数据出现乱序。现在,混合使用迭代和这些方法将触发 read*() 方法的 ValueError 异常。(由 Thomas Wouters 实现。)

  • struct 模块现在将结构格式字符串编译成内部表示并缓存此表示,从而提高了 20% 的速度。(由 Bob Ippolito 在 NeedForSpeed sprint 贡献。)

  • re 模块通过切换到 Python 的分配器函数而不是系统的 malloc()free(),获得了 1% 或 2% 的速度提升。(由 Jack Diederich 在 NeedForSpeed sprint 贡献。)

  • 代码生成器的窥孔优化器现在在表达式中执行简单的常量折叠。 如果你编写类似 a = 2+3 的代码,代码生成器将执行算术运算并生成与 a = 5 相对应的代码。(由 Raymond Hettinger 提出并实现。)

  • 函数调用现在更快了,因为代码对象现在将最近完成的帧(“僵尸帧”)保存在代码对象的内部字段中,以便下次调用该代码对象时重用。(最初由 Michael Hudson 提供的补丁,由 Armin Rigo 和 Richard Jones 修改;在 NeedForSpeed sprint 中提交。)帧对象也略小,这可能会提高缓存局部性并略微减少内存使用。(由 Neal Norwitz 贡献。)

  • Python 的内置异常现在是新式类,这一更改大大加快了实例化速度。因此,Python 2.5 中的异常处理速度比 2.4 快大约 30%。(由 Richard Jones、Georg Brandl 和 Sean Reifschneider 在 NeedForSpeed sprint 中贡献。)

  • 导入现在会缓存尝试过的路径,记录它们是否存在,以便解释器在启动时减少对 open()stat() 的调用次数。(由 Martin von Löwis 和 Georg Brandl 贡献。)

新的、改进的和删除的模块

标准库在 Python 2.5 中获得了许多增强功能和错误修复。以下是按模块名称字母顺序排列的最值得注意的更改的部分列表。 有关更完整的更改列表,请参阅源代码树中的 Misc/NEWS 文件,或查看 SVN 日志以了解所有详细信息。

  • audioop 模块现在支持 a-LAW 编码,并且 u-LAW 编码的代码已得到改进。(由 Lars Immisch 贡献。)

  • codecs 模块增加了对增量编解码器的支持。codec.lookup() 函数现在返回一个 CodecInfo 实例,而不是一个元组。CodecInfo 实例的行为类似于一个 4 元组以保持向后兼容性,但也具有属性 encodedecodeincrementalencoderincrementaldecoderstreamwriterstreamreader。增量编解码器可以接收输入并以多个块生成输出;输出与将整个输入馈送到非增量编解码器相同。有关详细信息,请参阅 codecs 模块文档。(由 Walter Dörwald 设计和实现。)

  • collections 模块新增了一种类型 defaultdict,它是标准 dict 类型的子类。 新类型的大部分行为类似于字典,但在键不存在时会构造一个默认值,并自动将其添加到请求的键值的字典中。

    defaultdict 构造函数的第一个参数是一个工厂函数,每当请求键但未找到时都会调用该函数。 此工厂函数不接收任何参数,因此可以使用内置类型构造函数,例如 list()int()。 例如,你可以像这样根据单词的首字母制作单词索引

    words = """Nel mezzo del cammin di nostra vita
    mi ritrovai per una selva oscura
    che la diritta via era smarrita""".lower().split()
    
    index = defaultdict(list)
    
    for w in words:
        init_letter = w[0]
        index[init_letter].append(w)
    

    打印 index 会产生以下输出

    defaultdict(<type 'list'>, {'c': ['cammin', 'che'], 'e': ['era'],
            'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'],
            'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'],
            'p': ['per'], 's': ['selva', 'smarrita'],
            'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']}
    

    (由 Guido van Rossum 贡献。)

  • collections 模块提供的 deque 双端队列类型现在具有 remove(value) 方法,该方法删除队列中首次出现的 value,如果未找到该值,则引发 ValueError。(由 Raymond Hettinger 贡献。)

  • 新模块:contextlib 模块包含与新的 ‘with’ 语句一起使用的辅助函数。 有关此模块的更多信息,请参阅 contextlib 模块 部分。

  • 新模块:cProfile 模块是现有 profile 模块的 C 实现,开销要低得多。 该模块的接口与 profile 相同:你可以运行 cProfile.run('main()') 来分析函数,可以将分析数据保存到文件中等等。目前尚不清楚 Hotshot 分析器(也用 C 编写,但不匹配 profile 模块的接口)是否会在以后的 Python 版本中继续维护。(由 Armin Rigo 贡献。)

    此外,用于分析分析器测量数据的 pstats 模块现在支持通过向 Stats 构造函数提供 stream 参数,将输出定向到任何文件对象。(由 Skip Montanaro 贡献。)

  • 解析逗号分隔值格式文件的 csv 模块收到了一些增强功能和一些错误修复。现在,可以通过调用 csv.field_size_limit(new_limit) 函数来设置字段的最大字节大小;省略 new_limit 参数将返回当前设置的限制。 reader 类现在具有一个 line_num 属性,用于计算从源读取的物理行数;记录可以跨越多行物理行,因此 line_num 与读取的记录数不同。

    CSV 解析器现在对多行带引号的字段更加严格。以前,如果一行在带引号的字段内结束而没有终止换行符,则会在返回的字段中插入换行符。当读取包含字段内回车符的文件时,此行为会导致问题,因此代码已更改为返回字段而不插入换行符。因此,如果嵌入在字段中的换行符很重要,则应以保留换行符的方式将输入拆分为行。

    (由 Skip Montanaro 和 Andrew McNamara 贡献。)

  • datetime 模块中的 datetime 类现在具有用于解析日期字符串的 strptime(string, format) 方法,由 Josh Spoerri 贡献。它使用与 time.strptime()time.strftime() 相同的格式字符

    from datetime import datetime
    
    ts = datetime.strptime('10:13:15 2006-03-07',
                           '%H:%M:%S %Y-%m-%d')
    
  • difflib 模块中的 SequenceMatcher.get_matching_blocks() 方法现在保证返回描述匹配子序列的最小块列表。 以前,该算法偶尔会将匹配元素的块分成两个列表条目。(由 Tim Peters 增强。)

  • doctest 模块增加了一个 SKIP 选项,该选项可以阻止示例代码被执行。 这旨在用于那些仅供读者参考的使用示例代码片段,而实际上不是测试用例的代码片段。

    testfile() 函数和 DocFileSuite 类中添加了一个 encoding 参数,用于指定文件的编码。 这使得在文档字符串中包含的测试中更容易使用非 ASCII 字符。(由 Bjorn Tillenius 贡献。)

  • email 包已更新至 4.0 版本。(由 Barry Warsaw 贡献。)

  • fileinput 模块变得更加灵活。现在支持 Unicode 文件名,并且为 input() 函数添加了一个默认值为 "r"mode 参数,允许以二进制或 通用换行符 模式打开文件。 另一个新参数 openhook 允许你使用 open() 以外的函数打开输入文件。 一旦你遍历完文件集,FileInput 对象的新 fileno() 将返回当前打开文件的文件描述符。(由 Georg Brandl 贡献。)

  • gc 模块中,新的 get_count() 函数返回一个 3 元组,其中包含三个 GC 代的当前收集计数。 这是垃圾收集器的记帐信息; 当这些计数达到指定阈值时,将进行垃圾回收扫描。 现有的 gc.collect() 函数现在接受一个可选的 generation 参数,其值为 0、1 或 2,用于指定要收集的代。(由 Barry Warsaw 贡献。)

  • heapq 模块中的 nsmallest()nlargest() 函数现在支持一个 key 关键字参数,该参数类似于 min()/max() 函数和 sort() 方法提供的参数。 例如

    >>> import heapq
    >>> L = ["short", 'medium', 'longest', 'longer still']
    >>> heapq.nsmallest(2, L)  # Return two lowest elements, lexicographically
    ['longer still', 'longest']
    >>> heapq.nsmallest(2, L, key=len)   # Return two shortest elements
    ['short', 'medium']
    

    (由 Raymond Hettinger 贡献。)

  • itertools.islice() 函数现在接受 None 作为 start 和 step 参数。 这使其与切片对象的属性更兼容,因此你现在可以编写以下代码

    s = slice(5)     # Create slice object
    itertools.islice(iterable, s.start, s.stop, s.step)
    

    (由 Raymond Hettinger 贡献。)

  • locale 模块中的 format() 函数已修改,并添加了两个新函数 format_string()currency()

    format() 函数的 val 参数以前可以是一个字符串,只要不多于一个 %char 说明符即可; 现在,该参数必须恰好是一个 %char 说明符,且没有周围的文本。 还添加了一个可选的 monetary 参数,如果为 True,则将使用区域设置的规则来格式化货币,在每三位数字之间放置一个分隔符。

    要格式化具有多个 %char 说明符的字符串,请使用新的 format_string() 函数,该函数的工作方式类似于 format(),但同时也支持将 %char 说明符与任意文本混合使用。

    还添加了一个新的 currency() 函数,该函数根据当前区域设置的设置格式化数字。

    (由 Georg Brandl 贡献。)

  • mailbox 模块进行了大规模重写,以添加修改邮箱的功能,此外还可以读取邮箱。 新的一组类(包括 mboxMHMaildir)用于读取邮箱,并具有 add(message) 方法来添加消息,remove(key) 来删除消息,以及 lock()/unlock() 来锁定/解锁邮箱。 以下示例将 maildir 格式的邮箱转换为 mbox 格式的邮箱

    import mailbox
    
    # 'factory=None' uses email.Message.Message as the class representing
    # individual messages.
    src = mailbox.Maildir('maildir', factory=None)
    dest = mailbox.mbox('/tmp/mbox')
    
    for msg in src:
        dest.add(msg)
    

    (由 Gregory K. Johnson 贡献。资金由 Google 的 2005 年编程之夏提供。)

  • 新模块:msilib 模块允许创建 Microsoft Installer .msi 文件和 CAB 文件。 还包括对读取 .msi 数据库的一些支持。(由 Martin von Löwis 贡献。)

  • nis 模块现在支持通过向 nis.match()nis.maps() 函数提供一个 domain 参数来访问系统默认域以外的域。(由 Ben Bell 贡献。)

  • operator 模块的 itemgetter()attrgetter() 函数现在支持多个字段。诸如 operator.attrgetter('a', 'b') 的调用将返回一个检索 ab 属性的函数。将此新功能与 sort() 方法的 key 参数结合使用,可以轻松地使用多个字段对列表进行排序。(由 Raymond Hettinger 贡献。)

  • optparse 模块已更新为 Optik 库的 1.5.1 版本。OptionParser 类获得了一个 epilog 属性,该属性是一个将在帮助消息之后打印的字符串,以及一个用于打破由对象创建的引用循环的 destroy() 方法。(由 Greg Ward 贡献。)

  • os 模块经历了一些更改。 stat_float_times 变量现在默认为 true,这意味着 os.stat() 现在将以浮点数的形式返回时间值。(这并不一定意味着 os.stat() 将返回精确到几分之一秒的时间;并非所有系统都支持这种精度。)

    添加了名为 os.SEEK_SETos.SEEK_CURos.SEEK_END 的常量; 这些是 os.lseek() 函数的参数。 两个用于锁定的新常量是 os.O_SHLOCKos.O_EXLOCK

    添加了两个新函数 wait3()wait4()。 它们类似于 waitpid() 函数,该函数等待子进程退出并返回进程 ID 及其退出状态的元组,但是 wait3()wait4() 返回额外的信息。wait3() 不接受进程 ID 作为输入,因此它等待任何子进程退出,并返回一个 3 元组,其中包含从 resource.getrusage() 函数返回的 process-idexit-statusresource-usagewait4(pid) 接受进程 ID。(由 Chad J. Schroeder 贡献。)

    在 FreeBSD 上,os.stat() 函数现在返回纳秒级分辨率的时间,并且返回的对象现在具有 st_genst_birthtime 属性。如果平台支持,st_flags 属性也可用。(由 Antti Louko 和 Diego Pettenò 贡献。)

  • pdb 模块提供的 Python 调试器现在可以存储在断点到达并停止执行时要执行的命令列表。创建断点 #1 后,输入 commands 1 并输入一系列要执行的命令,最后用 end 完成列表。命令列表可以包括恢复执行的命令,例如 continuenext。(由 Grégoire Dooms 贡献。)

  • picklecPickle 模块不再接受 __reduce__() 方法返回 None 值;该方法必须返回一个参数元组。返回 None 的功能在 Python 2.4 中已被弃用,因此这完成了该功能的移除。

  • 包含各种用于查找包的实用函数的 pkgutil 模块得到了增强,以支持 PEP 302 的导入钩子,现在也适用于存储在 ZIP 格式存档中的包。(由 Phillip J. Eby 贡献。)

  • Marc-André Lemburg 的 pybench 基准测试套件现在包含在 Tools/pybench 目录中。pybench 套件是对常用的 pystone.py 程序的改进,因为 pybench 提供了对解释器速度更详细的测量。它会测量诸如函数调用、元组切片、方法查找和数值运算等特定操作的时间,而不是像 pystone.py 那样执行许多不同的操作并将结果简化为单个数字。

  • pyexpat 模块现在使用 Expat 解析器的 2.0 版本。(由 Trent Mick 贡献。)

  • Queue 模块提供的 Queue 类获得了两个新方法。join() 会阻塞,直到队列中的所有项目都被检索并且对这些项目的所有处理工作都已完成。工作线程调用另一个新方法 task_done(),以表示某个项目的处理已完成。(由 Raymond Hettinger 贡献。)

  • 自 Python 2.0 以来就被弃用的旧 regexregsub 模块终于被删除。其他删除的模块:statcachetzparsewhrandom

  • 同样被删除的:包括 dircmpni 等古老模块的 lib-old 目录被删除。lib-old 不在默认的 sys.path 上,因此除非你的程序显式地将该目录添加到 sys.path 中,否则此删除不应影响你的代码。

  • rlcompleter 模块不再依赖于导入 readline 模块,因此现在可以在非 Unix 平台上工作。(来自 Robert Kiendl 的补丁。)

  • SimpleXMLRPCServerDocXMLRPCServer 类现在具有一个 rpc_paths 属性,该属性将 XML-RPC 操作限制为一组有限的 URL 路径;默认值是只允许 '/''/RPC2'。将 rpc_paths 设置为 None 或一个空元组会禁用此路径检查。

  • 由于 Philippe Biondi 的补丁,socket 模块现在在 Linux 上支持 AF_NETLINK 套接字。Netlink 套接字是一种 Linux 特有的机制,用于用户空间进程和内核代码之间的通信;一篇关于它们的介绍性文章位于 https://www.linuxjournal.com/article/7356。在 Python 代码中,netlink 地址表示为 2 个整数的元组,(pid, group_mask)

    套接字对象上的两个新方法 recv_into(buffer)recvfrom_into(buffer) 将接收到的数据存储在一个支持缓冲区协议的对象中,而不是将数据作为字符串返回。这意味着你可以将数据直接放入数组或内存映射文件中。

    套接字对象还获得了 getfamily()gettype()getproto() 访问器方法,用于检索套接字的系列、类型和协议值。

  • 新模块:spwd 模块提供了用于访问支持影子密码的系统上的影子密码数据库的函数。

  • struct 现在更快了,因为它将格式字符串编译为具有 pack()unpack() 方法的 Struct 对象。这类似于 re 模块如何让你创建编译的正则表达式对象。你仍然可以使用模块级别的 pack()unpack() 函数;它们将创建 Struct 对象并缓存它们。或者你可以直接使用 Struct 实例

    s = struct.Struct('ih3s')
    
    data = s.pack(1972, 187, 'abc')
    year, number, name = s.unpack(data)
    

    你还可以使用 pack_into(buffer, offset, v1, v2, ...)unpack_from(buffer, offset) 方法直接将数据打包和解包到缓冲区对象中。这让你能够直接将数据存储到数组或内存映射文件中。

    Struct 对象由 Bob Ippolito 在 NeedForSpeed sprint 中实现。对缓冲区对象的支持由 Martin Blais 在 NeedForSpeed sprint 中添加。)

  • 在 2.5 的开发过程中,Python 开发人员从 CVS 切换到了 Subversion。关于确切构建版本的信息可以作为 sys.subversion 变量使用,这是一个由 (解释器名称, 分支名称, 修订范围) 组成的 3 元组。例如,在撰写本文时,我的 2.5 副本报告 ('CPython', 'trunk', '45313:45315')

    此信息也可以通过 Py_GetBuildInfo() 函数提供给 C 扩展,该函数返回一个像这样的构建信息字符串:"trunk:45355:45356M, Apr 13 2006, 07:42:19"。(由 Barry Warsaw 贡献。)

  • 另一个新函数 sys._current_frames() 返回所有正在运行的线程的当前堆栈帧,以字典形式映射线程标识符到在调用该函数时该线程中当前处于活动状态的最顶层堆栈帧。(由 Tim Peters 贡献。)

  • tarfile 模块中的 TarFile 类现在有一个 extractall() 方法,该方法将存档中的所有成员提取到当前工作目录中。也可以将不同的目录设置为提取目标,并仅解压缩存档成员的子集。

    现在可以使用模式 'r|*' 自动检测以流模式打开的 tarfile 所使用的压缩。(由 Lars Gustäbel 贡献。)

  • 现在,threading 模块允许您设置创建新线程时使用的堆栈大小。stack_size([*size*]) 函数返回当前配置的堆栈大小,并提供可选的 size 参数来设置新值。并非所有平台都支持更改堆栈大小,但 Windows、POSIX 线程和 OS/2 都支持。(由 Andrew MacIntyre 贡献。)

  • unicodedata 模块已更新为使用 Unicode 字符数据库的 4.1.0 版本。某些规范需要 3.2.0 版本,因此它仍然可以通过 unicodedata.ucd_3_2_0 获得。

  • 新模块:uuid 模块根据 RFC 4122 生成通用唯一标识符(UUID)。RFC 定义了从起始字符串、系统属性或纯粹随机生成的几个不同的 UUID 版本。此模块包含一个 UUID 类和名为 uuid1()uuid3()uuid4()uuid5() 的函数,用于生成不同版本的 UUID。(RFC 4122 中未指定版本 2 UUID,此模块不支持。)

    >>> import uuid
    >>> # make a UUID based on the host ID and current time
    >>> uuid.uuid1()
    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
    
    >>> # make a UUID using an MD5 hash of a namespace UUID and a name
    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
    
    >>> # make a random UUID
    >>> uuid.uuid4()
    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
    
    >>> # make a UUID using a SHA-1 hash of a namespace UUID and a name
    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
    

    (由 Ka-Ping Yee 贡献。)

  • weakref 模块的 WeakKeyDictionaryWeakValueDictionary 类型获得了用于迭代字典中包含的弱引用的新方法。iterkeyrefs()keyrefs() 方法已添加到 WeakKeyDictionary,而 itervaluerefs()valuerefs() 已添加到 WeakValueDictionary。(由 Fred L. Drake, Jr. 贡献。)

  • webbrowser 模块收到了一些增强功能。现在它可以作为带有 python -m webbrowser 的脚本使用,并将 URL 作为参数;有一些开关来控制行为(-n 用于新的浏览器窗口,-t 用于新的标签页)。添加了新的模块级函数 open_new()open_new_tab() 来支持这一点。该模块的 open() 函数支持一个附加功能,即 autoraise 参数,该参数指示在可能的情况下是否提升打开的窗口。支持列表中添加了许多其他浏览器,例如 Firefox、Opera、Konqueror 和 elinks。(由 Oleg Broytmann 和 Georg Brandl 贡献。)

  • xmlrpclib 模块现在支持为 XML-RPC 日期类型返回 datetime 对象。为 loads() 函数或 Unmarshaller 类提供 use_datetime=True 以启用此功能。(由 Skip Montanaro 贡献。)

  • zipfile 模块现在支持 ZIP64 版本的格式,这意味着 .zip 存档现在可以大于 4 GiB,并且可以包含大于 4 GiB 的单个文件。(由 Ronald Oussoren 贡献。)

  • zlib 模块的 CompressDecompress 对象现在支持 copy() 方法,该方法会复制对象的内部状态并返回一个新的 CompressDecompress 对象。(由 Chris AtLee 贡献。)

ctypes 包

由 Thomas Heller 编写的 ctypes 包已添加到标准库中。ctypes 允许您调用共享库或 DLL 中的任意函数。长期用户可能还记得 dl 模块,该模块提供了加载共享库和在其中调用函数的函数。ctypes 包要高级得多。

要加载共享库或 DLL,您必须创建 CDLL 类的实例,并提供共享库或 DLL 的名称或路径。完成后,您可以通过将其作为 CDLL 对象的属性来调用任意函数。

import ctypes

libc = ctypes.CDLL('libc.so.6')
result = libc.printf("Line of output\n")

提供了各种 C 类型的类型构造函数:c_int()c_float()c_double()c_char_p()(等效于 char*)等等。与 Python 的类型不同,C 版本都是可变的;您可以分配给它们的 value 属性来更改包装的值。Python 整数和字符串将自动转换为相应的 C 类型,但对于其他类型,您必须调用正确的类型构造函数。(我的意思是必须;弄错通常会导致解释器因分段错误而崩溃。)

当 C 函数将修改内存区域时,您不应将 c_char_p() 与 Python 字符串一起使用,因为 Python 字符串应该是不可变的;违反此规则会导致令人困惑的错误。当您需要可修改的内存区域时,请使用 create_string_buffer()

s = "this is a string"
buf = ctypes.create_string_buffer(s)
libc.strfry(buf)

C 函数被假定为返回整数,但您可以设置函数对象的 restype 属性来更改这一点

>>> libc.atof('2.71828')
-1783957616
>>> libc.atof.restype = ctypes.c_double
>>> libc.atof('2.71828')
2.71828

ctypes 还为 Python 的 C API 提供了包装器,作为 ctypes.pythonapi 对象。此对象在调用函数之前不会释放全局解释器锁,因为在调用解释器代码时必须持有该锁。有一个 py_object 类型构造函数,它将创建一个 PyObject* 指针。一个简单的用法

import ctypes

d = {}
ctypes.pythonapi.PyObject_SetItem(ctypes.py_object(d),
          ctypes.py_object("abc"),  ctypes.py_object(1))
# d is now {'abc', 1}.

不要忘记使用 py_object();如果省略它,最终会导致分段错误。

ctypes 已经存在一段时间了,但人们仍然编写和分发手工编码的扩展模块,因为你不能依赖于 ctypes 的存在。 也许开发者会开始编写基于 ctypes 访问的库之上的 Python 包装器,而不是扩展模块,既然 ctypes 已经包含在核心 Python 中了。

另请参阅

https://web.archive.org/web/20180410025338/http://starship.python.net/crew/theller/ctypes/

预先添加到标准库的 ctypes 网页,包含教程、参考和常见问题解答。

ctypes 模块的文档。

ElementTree 包

Fredrik Lundh 的 ElementTree 库的一个子集,用于处理 XML,已作为 xml.etree 添加到标准库中。可用的模块是 ElementTree 1.2.6 中的 ElementTreeElementPathElementIncludecElementTree 加速模块也包含在内。

本节的其余部分将简要概述 ElementTree 的使用。ElementTree 的完整文档可在 https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm 找到。

ElementTree 将 XML 文档表示为元素节点的树。文档的文本内容存储为元素的 texttail 属性。(这是 ElementTree 和文档对象模型之间的主要区别之一;在 DOM 中,有许多不同类型的节点,包括 TextNode。)

最常用的解析函数是 parse(),它接受字符串(假设包含文件名)或类文件对象,并返回 ElementTree 实例。

from xml.etree import ElementTree as ET

tree = ET.parse('ex-1.xml')

feed = urllib.urlopen(
          'http://planet.python.org/rss10.xml')
tree = ET.parse(feed)

一旦你有了 ElementTree 实例,你可以调用其 getroot() 方法来获取根 Element 节点。

还有一个 XML() 函数,它接受一个字符串文字并返回一个 Element 节点(而不是 ElementTree)。此函数提供了一种整洁的方法来合并 XML 片段,接近 XML 文字的便利性。

svg = ET.XML("""<svg width="10px" version="1.0">
             </svg>""")
svg.set('height', '320px')
svg.append(elem1)

每个 XML 元素都支持一些类似字典和一些类似列表的访问方法。类似字典的操作用于访问属性值,而类似列表的操作用于访问子节点。

操作

结果

elem[n]

返回第 n 个子元素。

elem[m:n]

返回从第 m 个到第 n 个子元素的列表。

len(elem)

返回子元素的数量。

list(elem)

返回子元素的列表。

elem.append(elem2)

添加 elem2 作为子元素。

elem.insert(index, elem2)

在指定位置插入 elem2

del elem[n]

删除第 n 个子元素。

elem.keys()

返回属性名称的列表。

elem.get(name)

返回属性 name 的值。

elem.set(name, value)

为属性 name 设置新值。

elem.attrib

检索包含属性的字典。

del elem.attrib[name]

删除属性 name

注释和处理指令也表示为 Element 节点。要检查节点是否为注释或处理指令

if elem.tag is ET.Comment:
    ...
elif elem.tag is ET.ProcessingInstruction:
    ...

要生成 XML 输出,您应该调用 ElementTree.write() 方法。像 parse() 一样,它可以接受字符串或类文件对象

# Encoding is US-ASCII
tree.write('output.xml')

# Encoding is UTF-8
f = open('output.xml', 'w')
tree.write(f, encoding='utf-8')

(注意:输出使用的默认编码是 ASCII。对于一般的 XML 工作,元素的名称可能包含任意 Unicode 字符,ASCII 并不是很有用的编码,因为如果元素的名称包含任何大于 127 的字符,它会引发异常。因此,最好指定不同的编码,例如可以处理任何 Unicode 字符的 UTF-8。)

本节只是 ElementTree 接口的部分描述。请阅读软件包的官方文档以获取更多详细信息。

hashlib 包

Gregory P. Smith 编写的新的 hashlib 模块已添加,以替换 md5sha 模块。hashlib 添加了对其他安全哈希(SHA-224、SHA-256、SHA-384 和 SHA-512)的支持。在可用时,该模块使用 OpenSSL 来实现算法的快速平台优化实现。

旧的 md5sha 模块仍然作为 hashlib 的包装器存在,以保持向后兼容性。新模块的接口与旧模块的接口非常接近,但不完全相同。最重要的区别是用于创建新哈希对象的构造函数名称不同。

# Old versions
h = md5.md5()
h = md5.new()

# New version
h = hashlib.md5()

# Old versions
h = sha.sha()
h = sha.new()

# New version
h = hashlib.sha1()

# Hash that weren't previously available
h = hashlib.sha224()
h = hashlib.sha256()
h = hashlib.sha384()
h = hashlib.sha512()

# Alternative form
h = hashlib.new('md5')          # Provide algorithm as a string

一旦创建了哈希对象,其方法与以前相同:update(string) 将指定的字符串哈希到当前摘要状态,digest()hexdigest() 返回摘要值作为二进制字符串或十六进制数字字符串,并且 copy() 返回具有相同摘要状态的新哈希对象。

另请参阅

hashlib 模块的文档。

sqlite3 包

pysqlite 模块 (https://www.pysqlite.org),SQLite 嵌入式数据库的包装器,已添加到标准库中,包名为 sqlite3

SQLite 是一个 C 库,提供了一个轻量级的基于磁盘的数据库,它不需要单独的服务器进程,并允许使用 SQL 查询语言的非标准变体来访问数据库。一些应用程序可以使用 SQLite 进行内部数据存储。也可以使用 SQLite 对应用程序进行原型设计,然后将代码移植到更大的数据库(如 PostgreSQL 或 Oracle)。

pysqlite 由 Gerhard Häring 编写,并提供了符合 PEP 249 所描述的 DB-API 2.0 规范的 SQL 接口。

如果您自己编译 Python 源代码,请注意,源代码树不包含 SQLite 代码,只包含包装器模块。您需要在编译 Python 之前安装 SQLite 库和头文件,并且构建过程将在必要的头文件可用时编译该模块。

要使用该模块,您必须首先创建一个表示数据库的 Connection 对象。这里,数据将存储在 /tmp/example 文件中

conn = sqlite3.connect('/tmp/example')

您还可以提供特殊名称 :memory: 以在 RAM 中创建数据库。

一旦您拥有 Connection,您可以创建一个 Cursor 对象,并调用其 execute() 方法来执行 SQL 命令

c = conn.cursor()

# Create table
c.execute('''create table stocks
(date text, trans text, symbol text,
 qty real, price real)''')

# Insert a row of data
c.execute("""insert into stocks
          values ('2006-01-05','BUY','RHAT',100,35.14)""")

通常,您的 SQL 操作需要使用 Python 变量中的值。您不应该使用 Python 的字符串操作来组装查询,因为这样做是不安全的;它使您的程序容易受到 SQL 注入攻击。

请改用 DB-API 的参数替换。在需要使用值的地方放置 ? 作为占位符,然后将值元组作为游标的 execute() 方法的第二个参数提供。(其他数据库模块可能使用不同的占位符,例如 %s:1。)例如

# Never do this -- insecure!
symbol = 'IBM'
c.execute("... where symbol = '%s'" % symbol)

# Do this instead
t = (symbol,)
c.execute('select * from stocks where symbol=?', t)

# Larger example
for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
          ('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00),
          ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
         ):
    c.execute('insert into stocks values (?,?,?,?,?)', t)

要在执行 SELECT 语句后检索数据,您可以将游标视为迭代器,调用游标的 fetchone() 方法来检索单个匹配行,或调用 fetchall() 来获取匹配行的列表。

此示例使用迭代器形式

>>> c = conn.cursor()
>>> c.execute('select * from stocks order by price')
>>> for row in c:
...    print row
...
(u'2006-01-05', u'BUY', u'RHAT', 100, 35.140000000000001)
(u'2006-03-28', u'BUY', u'IBM', 1000, 45.0)
(u'2006-04-06', u'SELL', u'IBM', 500, 53.0)
(u'2006-04-05', u'BUY', u'MSOFT', 1000, 72.0)
>>>

有关 SQLite 支持的 SQL 方言的更多信息,请参阅 https://www.sqlite.org

另请参阅

https://www.pysqlite.org

pysqlite 的网页。

https://www.sqlite.org

SQLite 的网页;该文档描述了支持的 SQL 方言的语法和可用数据类型。

sqlite3 模块的文档。

PEP 249 - 数据库 API 规范 2.0

由 Marc-André Lemburg 编写的 PEP。

wsgiref 包

Web 服务器网关接口 (WSGI) v1.0 定义了 Web 服务器和 Python Web 应用程序之间的标准接口,并在 PEP 333 中进行了描述。wsgiref 包是 WSGI 规范的参考实现。

该软件包包含一个基本的 HTTP 服务器,它将运行 WSGI 应用程序;此服务器对于调试很有用,但不适用于生产用途。设置服务器只需要几行代码

from wsgiref import simple_server

wsgi_app = ...

host = ''
port = 8000
httpd = simple_server.make_server(host, port, wsgi_app)
httpd.serve_forever()

另请参阅

https://web.archive.org/web/20160331090247/http://wsgi.readthedocs.org/en/latest/

WSGI 相关资源的中心网站。

PEP 333 - Python Web 服务器网关接口 v1.0

由 Phillip J. Eby 编写的 PEP。

构建和 C API 更改

对 Python 的构建过程和 C API 的更改包括

  • Python 源代码树从 CVS 转换为 Subversion,这是一个复杂的迁移过程,由 Martin von Löwis 监督并完美执行。该过程被开发为 PEP 347

  • Coverity 是一家销售名为 Prevent 的源代码分析工具的公司,提供了他们对 Python 源代码进行检查的结果。分析发现了大约 60 个已快速修复的错误。许多错误是引用计数问题,通常发生在错误处理代码中。有关统计信息,请参阅 https://scan.coverity.com

  • C API 的最大更改来自 PEP 353,它修改了解释器以使用 Py_ssize_t 类型定义而不是 int。有关此更改的讨论,请参阅前面的部分 PEP 353:使用 ssize_t 作为索引类型

  • 字节码编译器的设计发生了很大变化,不再通过遍历解析树来生成字节码。相反,解析树被转换为抽象语法树(或 AST),并且遍历抽象语法树来生成字节码。

    Python 代码可以通过使用 compile() 内置函数并将 _ast.PyCF_ONLY_AST 指定为 flags 参数的值来获取 AST 对象

    from _ast import PyCF_ONLY_AST
    ast = compile("""a=0
    for i in range(10):
        a += i
    """, "<string>", 'exec', PyCF_ONLY_AST)
    
    assignment = ast.body[0]
    for_loop = ast.body[1]
    

    尚未为 AST 代码编写正式文档,但 PEP 339 讨论了该设计。要开始了解代码,请阅读 Parser/Python.asdl 中各种 AST 节点的定义。Python 脚本会读取此文件并在 Include/Python-ast.h 中生成一组 C 结构定义。PyParser_ASTFromString()PyParser_ASTFromFile() 定义在 Include/pythonrun.h 中,它们将 Python 源代码作为输入并返回表示内容的 AST 的根。然后可以通过 PyAST_Compile() 将此 AST 转换为代码对象。有关更多信息,请阅读源代码,然后在 python-dev 上提出问题。

    AST 代码是在 Jeremy Hylton 的管理下开发的,并由(按字母顺序排列)Brett Cannon、Nick Coghlan、Grant Edwards、John Ehresman、Kurt Kaiser、Neal Norwitz、Tim Peters、Armin Rigo 和 Neil Schemenauer 以及在 PyCon 等会议上的许多 AST 冲刺的参与者实现。

  • 应用了 Evan Jones 对 obmalloc 的补丁,该补丁首次在 2005 年的 PyCon DC 上的一次演讲中进行了描述。Python 2.4 在 256K 大小的区域中分配小对象,但从不释放区域。使用此补丁,当区域为空时,Python 将释放区域。最终效果是,在某些平台上,当您分配许多对象时,当您删除它们时,Python 的内存使用率实际上可能会下降,并且内存可能会返回给操作系统。(由 Evan Jones 实现,并由 Tim Peters 重新设计。)

    请注意,此更改意味着扩展模块在分配内存时必须更加小心。Python 的 API 有许多不同的内存分配函数,它们被分组为多个系列。例如,PyMem_Malloc()PyMem_Realloc()PyMem_Free() 是一个分配原始内存的系列,而 PyObject_Malloc()PyObject_Realloc()PyObject_Free() 是另一个应该用于创建 Python 对象的系列。

    以前,这些不同的系列都简化为平台的 malloc()free() 函数。这意味着如果你搞错了,使用 PyMem 函数分配了内存,但使用 PyObject 函数释放了它,也没有关系。使用 2.5 对 obmalloc 的更改,这些系列现在执行不同的操作,并且不匹配可能会导致段错误。您应该使用 Python 2.5 仔细测试您的 C 扩展模块。

  • 内置集合类型现在具有官方的 C API。调用 PySet_New()PyFrozenSet_New() 创建新集合,PySet_Add()PySet_Discard() 添加和删除元素,以及 PySet_Contains()PySet_Size() 来检查集合的状态。(由 Raymond Hettinger 贡献。)

  • C 代码现在可以通过调用 Py_GetBuildInfo() 函数来获取有关 Python 解释器确切修订版的信息,该函数返回一个类似于以下内容的构建信息字符串:"trunk:45355:45356M, Apr 13 2006, 07:42:19"。(由 Barry Warsaw 贡献。)

  • 可以使用两个新的宏来指示当前文件的局部 C 函数,以便可以使用更快的调用约定。Py_LOCAL(type) 将函数声明为返回指定 type 的值,并使用快速调用限定符。Py_LOCAL_INLINE(type) 执行相同的操作,并请求将函数内联。如果在包含 python.h 之前定义了宏 PY_LOCAL_AGGRESSIVE,则会为该模块启用一组更积极的优化;您应该对结果进行基准测试,以找出这些优化是否实际上使代码更快。(由 Fredrik Lundh 在 NeedForSpeed 冲刺中贡献。)

  • PyErr_NewException(name, base, dict) 现在可以接受一个基类元组作为其 base 参数。(由 Georg Brandl 贡献。)

  • 用于发出警告的 PyErr_Warn() 函数现在已弃用,取而代之的是 PyErr_WarnEx(category, message, stacklevel),它允许您指定此函数与调用方之间分隔的堆栈帧数。stacklevel 为 1 是调用 PyErr_WarnEx() 的函数,2 是该函数之上的函数,依此类推。(由 Neal Norwitz 添加。)

  • CPython 解释器仍然用 C 编写,但代码现在可以使用 C++ 编译器编译而不会出错。(由 Anthony Baxter、Martin von Löwis、Skip Montanaro 实现。)

  • 删除了 PyRange_New() 函数。它从未被记录,从未在核心代码中使用,并且具有危险的宽松错误检查。在您的扩展程序使用它的不太可能的情况下,您可以使用如下内容替换它

    range = PyObject_CallFunction((PyObject*) &PyRange_Type, "lll",
                                  start, stop, step);
    

特定于端口的更改

  • MacOS X (10.3 及更高版本):模块的动态加载现在使用 dlopen() 函数,而不是 MacOS 特有的函数。

  • MacOS X:在 configure 脚本中添加了 --enable-universalsdk 开关,该开关将解释器编译为可在 PowerPC 和 Intel 处理器上运行的通用二进制文件。(由 Ronald Oussoren 贡献;bpo-2573。)

  • Windows:不再支持将 .dll 作为扩展模块的文件名扩展名。 现在,只会搜索 .pyd 作为唯一的文件名扩展名。

移植到 Python 2.5

本节列出了先前描述的可能需要更改代码的更改

  • ASCII 现在是模块的默认编码。如果模块包含带有 8 位字符的字符串文字,但没有编码声明,则现在会出现语法错误。 在 Python 2.4 中,这会触发警告,而不是语法错误。

  • 之前,生成器的 gi_frame 属性始终是一个帧对象。 由于 PEP 342 部分中描述的更改,见 PEP 342:新的生成器功能,现在 gi_frame 有可能为 None

  • 当您尝试比较无法使用默认 ASCII 编码转换为 Unicode 的 Unicode 字符串和 8 位字符串时,会触发新的警告 UnicodeWarning 。 以前,此类比较会引发 UnicodeDecodeError 异常。

  • 库:csv 模块现在对多行带引号的字段更加严格。 如果您的文件在字段中嵌入了换行符,则应以保留换行符的方式将输入拆分为行。

  • 库:locale 模块的 format() 函数以前会接受任何字符串,只要不超过一个 %char 说明符。 在 Python 2.5 中,参数必须只有一个 %char 说明符,并且没有周围的文本。

  • 库:picklecPickle 模块不再接受来自 __reduce__() 方法的 None 返回值; 该方法必须返回一个参数元组。 这些模块也不再接受已弃用的 *bin* 关键字参数。

  • 库:SimpleXMLRPCServerDocXMLRPCServer 类现在有一个 rpc_paths 属性,该属性将 XML-RPC 操作限制为一组有限的 URL 路径;默认值是仅允许 '/''/RPC2'。 将 rpc_paths 设置为 None 或空元组将禁用此路径检查。

  • C API:现在许多函数使用 Py_ssize_t 而不是 int,以便在 64 位计算机上处理更多数据。 扩展代码可能需要进行相同的更改,以避免警告并支持 64 位计算机。 有关此更改的讨论,请参阅前面的章节 PEP 353:将 ssize_t 用作索引类型

  • C API:obmalloc 的更改意味着您必须小心不要混合使用 PyMem_*PyObject_* 系列函数。 使用一个系列的 *_Malloc 分配的内存必须使用相应系列的 *_Free 函数释放。

鸣谢

作者要感谢以下人员为本文的各种草稿提供建议、更正和帮助:Georg Brandl、Nick Coghlan、Phillip J. Eby、Lars Gustäbel、Raymond Hettinger、Ralf W. Grosse-Kunstleve、Kent Johnson、Iain Lowe、Martin von Löwis、Fredrik Lundh、Andrew McNamara、Skip Montanaro、Gustavo Niemeyer、Paul Prescod、James Pryor、Mike Rovner、Scott Weikart、Barry Warsaw、Thomas Wouters。