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: 条件表达式

长期以来,人们一直要求有一种编写条件表达式的方法,这种表达式根据布尔值是真还是假来返回 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

求值仍然是惰性的,就像现有的布尔表达式一样,所以求值顺序会有点跳跃。中间的 *条件* 表达式首先被求值,*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 - 条件表达式

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

PEP 309: 部分函数应用

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

此模块中的一个有用工具是 partial() 函数。对于以函数式风格编写的程序,有时你会希望构造现有函数的变体,其中某些参数已填充。考虑一个 Python 函数 f(a, b, c);你可以创建一个新函数 g(b, c),它等同于 f(1, 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),
     )

Python 包索引 https://pypi.ac.cn 的另一项新增强功能是存储包的源和二进制归档文件。新的 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: 绝对导入和相对导入

PEP 328 的简单部分已在 Python 2.4 中实现:现在可以使用括号将使用 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 安装中都可用。

阅读依赖相对导入的代码也比较不清晰,因为读者可能会对意图使用哪个模块(stringpkg.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 形式的 import 语句一起使用,只能与 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 - 将模块作为脚本执行

由 Nick Coghlan 撰写和实现。

PEP 341: 统一的 try/except/finally

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

Guido van Rossum 花了一些时间研究 Java,它确实支持结合 except 块和 finally 块的等效功能,这澄清了该语句的含义。在 Python 2.5 中,你现在可以这样写:

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

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

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

参见

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

PEP 由 Georg Brandl 编写;由 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 异常以终止迭代。收到此异常后,生成器的代码必须要么引发 GeneratorExit,要么引发 StopIteration。捕获 GeneratorExit 异常并返回值是非法的,并将触发 RuntimeError;如果函数引发其他异常,则该异常将传播给调用者。close() 也将在生成器被垃圾回收时由 Python 的垃圾回收器调用。

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

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

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

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

这一变化还有一个更深奥的影响:以前,生成器的 gi_frame 属性始终是一个帧对象。现在,一旦生成器耗尽,gi_frame 可能是 None

参见

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

PEP 由 Guido van Rossum 和 Phillip J. Eby 撰写;由 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* 名称。(请注意,*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 ...

该语句执行后,即使 for 循环在块执行过程中引发了异常,*f* 中的文件对象也会自动关闭。

备注

在这种情况下,*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* 引发异常,则会使用异常详细信息调用 __exit__(type, value, traceback),这些值与 sys.exc_info() 返回的值相同。该方法的返回值控制是否重新引发异常:任何假值都会重新引发异常,而 True 将导致抑制异常。你很少会想要抑制异常,因为如果你这样做,包含 'with' 语句的代码作者将永远不会意识到出了问题。

  • 如果 *BLOCK* 没有引发异常,__exit__() 方法仍会被调用,但 *type*、*value* 和 *traceback* 都为 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” 语句

PEP 由 Guido van Rossum 和 Nick Coghlan 撰写;由 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 2.5 中,将字符串作为异常引发,例如语句 raise "Error occurred",已被弃用,并将触发警告。目标是在未来几个版本中删除字符串异常功能。Python 3.0 的目标是要求任何作为异常引发的类都必须派生自 BaseException 或其子孙类,Python 2.x 系列的未来版本可能会开始强制执行此约束。因此,我建议您现在就开始让所有异常类都派生自 Exception。有人建议在 Python 3.0 中删除裸的 except: 形式,但 Guido van Rossum 尚未决定是否这样做。

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

参见

PEP 352 - 异常的必需超类

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

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

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

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 作为索引类型

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

PEP 357: '__index__' 方法

NumPy 开发者遇到了一个只能通过添加一个新的特殊方法 __index__() 来解决的问题。当使用切片表示法时,例如 [start:stop:step],*start*、*stop* 和 *step* 索引的值都必须是整数或长整数。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 - 允许任意对象用于切片

PEP 由 Travis Oliphant 编写并实现。

其他语言更改

以下是 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 代表“reverse”(反向)。

    一些例子

    >>> ('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() 内置函数获得了类似于 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。比较结果为 false

    >>> 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 冲刺活动中贡献。)

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

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

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

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

  • 导入现在会缓存尝试过的路径,记录它们是否存在,以便解释器在启动时进行更少的 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() 函数现在接受一个可选的 0、1 或 2 的 generation 参数,以指定要收集哪一代。(由 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 年 Summer of Code 提供。)

  • 新模块: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 作为输入,因此它等待任何子进程退出并返回 进程 ID退出状态资源使用情况 的 3 元组,其值由 resource.getrusage() 函数返回。wait4(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 贡献。)

  • 旧的 regexregsub 模块(自 Python 2.0 以来已弃用)最终已被删除。其他已删除的模块:statcachetzparsewhrandom

  • 同样删除的还有 lib-old 目录,其中包含一些旧模块,例如 dircmpnilib-old 不在默认的 sys.path 中,因此除非您的程序明确将该目录添加到 sys.path,否则此删除应该不会影响您的代码。

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

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

  • socket 模块现在支持 Linux 上的 AF_NETLINK 套接字,这要归功于 Philippe Biondi 的补丁。Netlink 套接字是 Linux 特有的用户空间进程与内核代码之间通信的机制;有关它们的介绍性文章位于 https://linuxjournal.cn/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 中。)

  • Python 开发者在 2.5 开发过程中从 CVS 切换到 Subversion。关于确切构建版本的信息以 sys.subversion 变量的形式提供,它是一个 3 元组 (interpreter-name, branch-name, revision-range)。例如,在编写本文时,我的 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() 方法已添加到 WeakKeyDictionaryitervaluerefs()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 还通过 ctypes.pythonapi 对象为 Python 的 C API 提供了包装器。此对象在调用函数之前*不会*释放全局解释器锁,因为在调用解释器代码时必须持有该锁。有一个 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/

pre-stdlib ctypes 网页,包含教程、参考和常见问题解答。

ctypes 模块的文档。

ElementTree 包

Fredrik Lundh 的 ElementTree XML 处理库的一个子集已作为 xml.etree 添加到标准库中。可用的模块是 ElementTree 1.2.6 中的 ElementTreeElementPathElementInclude。还包括 cElementTree 加速模块。

本节的其余部分将简要概述 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 的字符。因此,最好指定不同的编码,例如 UTF-8,它可以处理任何 Unicode 字符。)

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

hashlib 包

一个新的 hashlib 模块,由 Gregory P. Smith 编写,已添加以替换 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 Server Gateway Interface (WSGI) v1.0 定义了 Web 服务器和 Python Web 应用程序之间的标准接口,并在 PEP 333 中有所描述。wsgiref 包是 WSGI 规范的参考实现。

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

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 结构定义。在 Include/pythonrun.h 中定义的 PyParser_ASTFromString()PyParser_ASTFromFile() 将 Python 源代码作为输入,并返回表示内容的 AST 根。然后,此 AST 可以通过 PyAST_Compile() 转换为代码对象。有关更多信息,请阅读源代码,然后向 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 的补丁,最初在 PyCon DC 2005 的演讲中描述,已被应用。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 的更改,这些家族现在执行不同的操作,不匹配可能会导致段错误。您应该仔细测试您的 C 扩展模块与 Python 2.5 的兼容性。

  • 内置的集合类型现在有一个官方的 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

  • 当您尝试比较 Unicode 字符串和无法使用默认 ASCII 编码转换为 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。