# 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）。所有这些改进都是有价值的，但它们都是对特定语言功能的改进； none of them are broad modifications to Python's semantics.

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

本文并不试图成为新功能的完整规范；相反，通过有帮助的示例简要介绍了这些变更。有关完整详情，你应始终参考Python 2.5的文档，网址为https://docs.python.org。如果你想了解完整的实现和设计原理，请参考特定新功能的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_v``、`if 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 语言的语法中要求括号，但出于风格考虑，我认为你应该总是使用它们。考虑以下两个语句:

    # 第一个版本 -- 无括号
    level = 1 if logging else 0

    # 第二个版本 -- 有括号
    level = (1 if logging else 0)

在第一个版本中，我认为读者的视线可能会将语句分为'level = 1'、'if logging'、'else 0'，并认为条件决定了是否对*level*进行赋值。在我看来，第二个版本读起来更好，因为它清楚地表明赋值始终执行，只是在两个值之间进行选择。

包含括号还有一个原因：一些列表推导式和lambda表达式的奇怪组合可能看起来像错误的条件表达式。参见 PEP 308 了解一些示例。如果你在条件表达式周围加上括号，就不会遇到这种情况。

参见

PEP 308 - 条件表达式

  PEP 由 Guido van Rossum 和 Raymond D 撰写，由 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):
        "将 'message' 的内容写到指定的子系统。"
        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()` 函数现在有了 `requires`、`provides` 和 `obsoletes` 关键字参数。当你使用 `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.org 的另一个新增强功能是存储包的源代码和二进制存档。新的 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.main` 和 `pkg.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` 形式时在模块名称前添加一个前导点，仍然可以进行相对导入：

    # 从 pkg.string 导入名称
    from .string import name1, name2
    # 导入 pkg.string
    from . import string

这会相对当前包导入 `string` 模块，所以在 `pkg.main` 中这将导入 name1 和 name2 来自 `pkg.string`。额外的前导点号从当前包的父包开始执行相对导入。例如，在 `A.B.C` 模块中的代码可以这样做:

    from . import D                 # 导入 A.B.D
    from .. import E                # 导入 A.E
    from ..F import G               # 导入 A.F.G

开头的句点不可用于 import 语句的 `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，后者支持组合 `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

  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 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 实现。包括一些更高级的使用生成器作为协程的示例。

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

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

  协程的Wikipedia条目。

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

  基于 Perl 视角对协程的介绍，由 Dan Sugalski 撰写。

## 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
            ... 更多处理代码 ...

在此语句被执行之后，文件对象 f 将被自动关闭，即使是当 `for` 循环在代码块中间位置引发了异常的时候也是如此。

备注

在此情况下，f 就是由 `open()` 所创建的对象，因为 `__enter__()` 会返回 self。

`threading` 模块的加锁和条件变量也支持 '`with`' 语句:

    lock = threading.Lock()
    with lock:
        # 关键代码段
        ...

这个锁会在代码块被执行之前锁定并总是会在代码块完成之后释放。

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

    from decimal import Decimal, Context, localcontext

    # 使用默认精度 28 位显示
    v = Decimal('578')
    print v.sqrt()

    with localcontext(Context(prec=16)):
        # 此块中的所有代码使用 16 位精度。
        # 块退出时恢复原始上下文。
        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 ...')
        # ... 更多操作 ...

如果块中的代码完美运行，事务应该被提交；如果出现异常，事务应该被回滚。以下是我假设的 `DatabaseConnection` 的基本接口：

    class DatabaseConnection:
        # 数据库接口
        def cursor(self):
            "返回一个游标对象并开始一个新的事务"
        def commit(self):
            "提交当前事务"
        def rollback(self):
            "回滚当前事务"

The `__enter__()` 方法非常简单，只需开始一个新的事务。对于这个应用程序，生成的游标对象将是一个有用的结果，因此该方法将返回它。用户可以在他们的 '`with`' 语句中添加 `as cursor` 来将游标绑定到一个变量名。:

    class DatabaseConnection:
        ...
        def __enter__(self):
            # 开始一个新事务的代码
            cursor = self.cursor()
            return cursor

The `__exit__()` 方法是最复杂的，因为大部分工作需要在这里完成。该方法需要检查是否发生了异常。如果没有异常，事务将被提交。如果发生了异常，事务将被回滚。

在下面的代码中，执行将直接从函数末尾跳出，返回默认值 `None`。`None` 是假值，因此异常将自动重新抛出。如果你愿意，你可以更明确地添加一个 `return` 语句在标记的位置。:

    class DatabaseConnection:
        ...
        def __exit__ (self, type, value, tb):
            if tb is None:
                # 没有异常，因此提交
                self.commit()
            else:
                # 发生异常，因此回滚。
                self.rollback()
                # 返回 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` 类和所有标准内置异常（`NameError`、`ValueError` 等）现在都是新式类。

异常的继承层次结构已经稍微重新排列了一下。在2.5中，继承关系如下:

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

这种重新排列是因为人们通常希望捕获所有表示程序错误的异常。然而，`KeyboardInterrupt` 和 `SystemExit` 并不是错误，它们通常表示用户执行了明确的操作，例如按下 `Control`-`C` 或代码调用 `sys.exit()`。一个裸露的 `except:` 会捕获所有异常，因此通常需要列出 `KeyboardInterrupt` 和 `SystemExit` 以重新抛出它们。通常的模式是:

    try:
        ...
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        # 记录错误...
        # 继续运行程序...

在 Python 2.5 中，你现在可以写 `except Exception` 来达到同样的效果，捕获所有通常表示错误的异常，但不包括 `KeyboardInterrupt` 和 `SystemExit`。与之前的版本一样，裸露的 `except:` 仍然会捕获所有异常。

Python 3.0 的目标要求任何作为异常抛出的类都必须从 `BaseException` 或 `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_t`。`PyArg_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]   # 输出 1, 2
      print d[3], d[4]   # 输出 0, 0

- 8位字符串和Unicode字符串都新增了 `partition(sep)` 和 `rpartition(sep)` 方法，简化了常见的用例。

  `find(S)` 方法通常用于获取一个索引，然后使用该索引来切片字符串，获取分隔符之前和之后的部分。`partition(sep)` 将这种模式浓缩为单个方法调用，返回一个包含分隔符之前子字符串、分隔符本身和分隔符之后子字符串的3元组。如果找不到分隔符，元组的第一个元素是整个字符串，其他两个元素为空。`rpartition(sep)` 也返回一个3元组，但从字符串末尾开始搜索；`r` 代表 'reverse'。

  示例如下：

      >>> ('http://www.python.org').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']
      # 打印 'longest'
      print max(L, key=len)
      # 打印 'short'，因为在字典序上 'short' 的值最大
      print max(L)

  （由 Steven Bethard 和 Raymond Hettinger 贡献。）

- 两个新的内置函数，`any()` 和 `all()`，用于评估迭代器中是否包含任何真值或假值。`any()` 如果迭代器返回的任何值为真，则返回 `True`；否则返回 `False`。`all()` 只有当迭代器返回的所有值都为真时，才返回 `True`。（由 Guido van Rossum 建议，Raymond Hettinger 实现。）

- 类的 `__hash__()` 方法的返回结果现在可以是长整数或常规整数。如果返回的是长整数，则取该值的哈希。在早期版本中，哈希值必须是常规整数，但在 2.5 版中，`id()` 内置函数被改为总是返回非负数，用户通常在 `__hash__()` 方法中使用 `id(self)` (尽管不推荐这样做) 。

- ASCII 现在是模块的默认编码。如果模块包含带有 8 位字符的字符串字面量但没有编码声明，则会引发语法错误。在 Python 2.4 中，这会触发警告，而不是语法错误。参见 PEP 263 了解如何声明模块的编码；例如，你可以在源文件顶部附近添加如下行：

      # -*- coding: latin1 -*-

- 一个新的警告，`UnicodeWarning`，在你尝试比较一个 Unicode 字符串和一个无法使用默认 ASCII 编码转换为 Unicode 的 8 位字符串时触发。比较的结果为假：

      >>> chr(128) == unichr(128)   # 无法将 chr(128) 转换为 Unicode
      __main__:1: UnicodeWarning: Unicode 等值比较失败
        无法将两个参数都转换为 Unicode - 解释为不等
      False
      >>> chr(127) == unichr(127)   # chr(127) 可以转换
      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 实现。）

### 交互解释器变更

在交互式解释器中，`quit` 和 `exit` 长期以来一直是字符串，以便新用户在尝试退出时得到一些有帮助的消息:

    >>> quit
    '使用Ctrl-D（即EOF）退出。'

在Python 2.5中，`quit` 和 `exit` 现在是对象，它们仍然产生自己的字符串表示，但也是可调用的。新手尝试 `quit()` 或 `exit()` 现在将如他们所期望的那样退出解释器。（由Georg Brandl实现。）

Python可执行文件现在接受标准的长选项 `--help` 和 `--version`；在Windows上，它还接受 `/?` 选项来显示帮助消息。（由 Georg Brandl 实现。）

### 性能优化

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

- 在Python 2.4中引入时，内置的 `set` 和 `frozenset` 类型是基于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 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 中的标准库收到了许多增强和 bug 修复。以下是按模块名称字母顺序排序的最显著变化的部分列表。查阅源树中的 `Misc/NEWS` 文件以获取更完整的变化列表，或通过 SVN 日志查看所有详细信息。

- `audioop` 模块现在支持 a-LAW 编码，并且 u-LAW 编码的代码已得到改进。（由 Lars Immisch 贡献。）

- `codecs` 模块增加了对增量编解码器的支持。`codec.lookup()` 函数现在返回一个 `CodecInfo` 实例，而不是一个元组。`CodecInfo` 实例表现得像一个4元组，以保持向后兼容性，但同时也具有以下属性：`encode`、`decode`、`incrementalencoder`、`incrementaldecoder`、`streamwriter` 和 `streamreader`。增量编解码器可以分多次接收输入并生成输出；输出与非增量编解码器一次性接收全部输入的输出相同。详细信息请参阅 `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 贡献。）

- `deque` 双端队列类型由 `collections` 模块提供，现在新增了 `remove(value)` 方法，用于移除队列中第一次出现的 value，如果未找到该值则抛出 `ValueError`。（由 Raymond Hettinger 贡献。）

- 新模块：`contextlib` 模块包含用于新 '`with`' 语句的辅助函数。更多信息请参见 contextlib 模块 部分。

- New module: The `cProfile` module is a C implementation of the existing `profile` module that has much lower overhead. The module's interface is the same as `profile`: you run `cProfile.run('main()')` to profile a function, can save profile data to a file, etc. It's not yet known if the Hotshot profiler, which is also written in C but doesn't match the `profile` module's interface, will continue to be maintained in future versions of Python. (Contributed by Armin Rigo.)

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

- `csv` 模块用于解析逗号分隔值格式的文件，收到了多项增强和多个 bug 修复。你现在可以通过调用 `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()` 函数返回一个包含三个 GC 世代当前收集计数的 3 元组。这是垃圾收集器的统计信息；当这些计数达到指定阈值时，将进行垃圾收集扫描。现有的 `gc.collect()` 函数现在接受一个可选的 generation 参数，值为 0、1 或 2，以指定要收集的世代。（由 Barry Warsaw 贡献。）

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

      >>> import heapq
      >>> L = ["short", 'medium', 'longest', 'longer still']
      >>> heapq.nsmallest(2, L)  # 按字典序返回两个最低的元素
      ['longer still', 'longest']
      >>> heapq.nsmallest(2, L, key=len)   # 返回两个最短的元素
      ['short', 'medium']

  （由 Raymond Hettinger 贡献。）

- `itertools.islice()` 函数现在接受 `None` 作为 start 和 step 参数。这使得它与切片对象的属性更加兼容，因此你现在可以写出以下代码：

      s = slice(5)     # 创建切片对象
      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` 模块进行了大规模重写，以增加修改邮箱的能力，而不仅仅是读取邮箱。一组新的类，包括 `mbox`、`MH` 和 `Maildir`，用于读取邮箱，并具有 `add(message)` 方法来添加消息，`remove(key)` 来删除消息，以及 `lock()`/`unlock()` 来锁定/解锁邮箱。以下示例将 maildir 格式的邮箱转换为 mbox 格式的邮箱：

      import mailbox

      # 'factory=None' 使用 email.Message.Message 作为表示
      # 单个消息的类。
      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')` 将返回一个函数，该函数检索 `a` 和 `b` 属性。结合这一新特性与 `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_SET`、`os.SEEK_CUR` 和 `os.SEEK_END` 的常量；这些是 `os.lseek()` 函数的参数。用于锁定的两个新常量是 `os.O_SHLOCK` 和 `os.O_EXLOCK`。

  添加了两个新函数 `wait3()` 和 `wait4()`。它们类似于 `waitpid()` 函数，后者等待子进程退出并返回进程 ID 及其退出状态的元组，但 `wait3()` 和 `wait4()` 返回更多信息。`wait3()` 不接受进程 ID 作为输入，因此它等待任何子进程退出并返回一个 3 元组，包括 process-id、exit-status 和 resource-usage，这些信息来自 `resource.getrusage()` 函数。`wait4(pid)` 接受进程 ID。（由 Chad J. Schroeder 贡献。）

  在 FreeBSD 上，`os.stat()` 函数现在返回具有纳秒分辨率的时间，返回的对象现在包含 `st_gen` 和 `st_birthtime`。如果平台支持，`st_flags` 属性也可用。（由 Antti Louko 和 Diego Pettenò 贡献。）

- `pdb` 模块提供的 Python 调试器现在可以存储一系列命令，当达到断点并停止执行时执行这些命令。一旦创建了断点 #1，输入 `commands 1` 并输入一系列要执行的命令，最后用 `end` 结束列表。命令列表可以包括恢复执行的命令，如 `continue` 或 `next`。（由 Grégoire Dooms 贡献。）

- `pickle` 和 `cPickle` 模块不再接受 `__reduce__()` 方法返回的 `None` 值；该方法必须返回一个参数元组。在 Python 2.4 中已弃用返回 `None` 的能力，因此这完成了该特性的移除。

- `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 起已被弃用的旧模块 `regex` 和 `regsub` 终于被删除。其他被删除的模块包括：`statcache`、`tzparse`、`whrandom`。

- 同样被删除的还有 `lib-old` 目录，该目录包含古老的模块如 `dircmp` 和 `ni`，已被移除。`lib-old` 不在默认的 `sys.path` 中，因此除非你的程序显式将该目录添加到 `sys.path`，否则此次移除不应影响你的代码。

- `rlcompleter` 模块不再依赖于导入 `readline` 模块，因此现在可以在非 Unix 平台上工作。（由 Robert Kiendl 提供的补丁。）

- `SimpleXMLRPCServer` 和 `DocXMLRPCServer` 类现在有一个 `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 地址表示为两个整数的元组 `(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 在同一 sprint 中添加。）

- Python 开发者在 2.5 开发过程中从 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()` 方法，用于将存档中的所有成员提取到当前工作目录。也可以设置不同的目录作为提取目标，并且可以选择仅解压存档的子集成员。

  在流模式下打开的 tarfile 的压缩类型现在可以通过模式 `'r|*'` 自动检测。 (由 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。（版本 2 的 UUID 在 RFC 4122 中未指定，且此模块不支持。）:

      >>> import uuid
      >>> # 基于主机 ID 和当前时间生成一个 UUID
      >>> uuid.uuid1()
      UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

      >>> # 使用命名空间 UUID 和名称的 MD5 哈希生成一个 UUID
      >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
      UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')

      >>> # 生成一个随机 UUID
      >>> uuid.uuid4()
      UUID('16fd2706-8baf-433b-82eb-8c7fada847da')

      >>> # 使用命名空间 UUID 和名称的 SHA-1 哈希生成一个 UUID
      >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
      UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')

  （由 Ka-Ping Yee 贡献。）

- `weakref` 模块的 `WeakKeyDictionary` 和 `WeakValueDictionary` 类型新增了用于迭代字典中包含的弱引用的方法。`WeakKeyDictionary` 添加了 `iterkeyrefs()` 和 `keyrefs()` 方法，`WeakValueDictionary` 添加了 `itervaluerefs()` 和 `valuerefs()` 方法。（由 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` 模块的 `Compress` 和 `Decompress` 对象现在支持 `copy()` 方法，该方法复制对象的内部状态并返回一个新的 `Compress` 或 `Decompress` 对象。（由 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字符串应该是不可变的；违反这一规则会导致令人困惑的bug。当你需要一个可修改的内存区域时，请使用 `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 现在是 {'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`、`ElementPath` 和来自 ElementTree 1.2.6 的 `ElementInclude`。`cElementTree` 加速模块也包括在内。

本节的剩余部分将提供使用 ElementTree 的简要说明。 要获取 ElementTree 的完整文档可访问 https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm。

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

最常用的解析函数是 `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()` 类似，它既可以接受一个字符串，也可以接受一个类似文件的对象：

    # 编码为 US-ASCII
    tree.write('output.xml')

    # 编码为 UTF-8
    f = open('output.xml', 'w')
    tree.write(f, encoding='utf-8')

（注意：输出的默认编码是 ASCII。对于一般的 XML 工作，其中元素的名称可能包含任意的 Unicode 字符，ASCII 不是一个非常有用的编码，因为它会在元素名称包含任何值大于 127 的字符时引发异常。因此，最好指定一个不同的编码，如 UTF-8，它可以处理任何 Unicode 字符。）

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

参见

https://web.archive.org/web/20201124024954/http://effbot.org/zone/element-index.htm

  ElementTree 的官方文档。

### hashlib 包

由 Gregory P. Smith 编写的一个新的 `hashlib` 模块已被添加，以替换 `md5` 和 `sha` 模块。`hashlib` 增加了对额外安全哈希（SHA-224、SHA-256、SHA-384 和 SHA-512）的支持。在可用的情况下，该模块使用 OpenSSL 以实现快速的平台优化算法实现。

旧的 `md5` 和 `sha` 模块仍然存在，作为 hashlib 的包装器以保持向后兼容性。新模块的接口与旧模块非常接近，但不完全相同。最显著的区别是用于创建新哈希对象的构造函数命名不同。:

    # 旧版本
    h = md5.md5()
    h = md5.new()

    # 新版本
    h = hashlib.md5()

    # 旧版本
    h = sha.sha()
    h = sha.new()

    # 新版本
    h = hashlib.sha1()

    # 之前不可用的哈希
    h = hashlib.sha224()
    h = hashlib.sha256()
    h = hashlib.sha384()
    h = hashlib.sha512()

    # 备选形式
    h = hashlib.new('md5')          # 以字符串形式提供算法

一旦创建了哈希对象，其方法与之前相同：`update(string)` 将指定的字符串哈希到当前摘要状态中，`digest()` 和 `hexdigest()` 分别返回二进制字符串或十六进制数字字符串的摘要值，`copy()` 返回具有相同摘要状态的新哈希对象。

参见

`hashlib` 模块的文档。

### sqlite3 包

pysqlite 模块（https://www.pysqlite.org），作为 SQLite 嵌入式数据库的包装器，已被添加到标准库中，包名为 `sqlite3`。

SQLite 是一个C语言库，它可以提供一种轻量级的基于磁盘的数据库，这种数据库不需要独立的服务器进程，也允许需要使用一种非标准的 SQL 查询语言来访问它。一些应用程序可以使用 SQLite 作为内部数据存储。可以用它来创建一个应用程序原型，然后再迁移到更大的数据库，比如 PostgreSQL 或 Oracle。

pysqlite 由 Gerhard Häßing 编写，提供了一个符合 PEP 249 描述的 DB-API 2.0 规范的 SQL 接口。

如果你自己编译 Python 源代码，请注意源代码树不包含 SQLite 代码，只包含封装模块。在编译 Python 之前，你需要安装 SQLite 库和头文件，当必要的头文件可用时，编译过程将编译模块。

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

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

你也可以使用 `:memory:` 来创建一个内存中的数据库。

一旦你有了 `Connection`，你可以创建一个 `Cursor` 对象并调用其 `execute()` 方法来执行 SQL 命令:

    c = conn.cursor()

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

    # 插入一行数据
    c.execute("""insert into stocks
              values ('2006-01-05','BUY','RHAT',100,35.14)""")

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

相反，使用 DB-API 的参数替换。在需要使用值的任何位置放置 `?` 作为占位符，然后将值的元组作为第二个参数传递给游标的 `execute()` 方法。（其他数据库模块可能使用不同的占位符，例如 `%s` 或 `:1`。）例如：

    # 千万不要这样做 -- 不安全！
    symbol = 'IBM'
    c.execute("... where symbol = '%s'" % symbol)

    # 应该这样做
    t = (symbol,)
    c.execute('select * from stocks where symbol=?', t)

    # 更大的示例
    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 - DB-API 2.0 规范

  PEP 由 Marc-André Lemburg 撰写。

### 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

  PEP 由 Phillip J. Eby 撰写。

## 构建和 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 的值并使用快速调用限定符。 `Py_LOCAL_INLINE` 执行相同的操作并还将请求函数是内联的。 如果在包括 `python.h` 之前 `PY_LOCAL_AGGRESSIVE` 已被定义，则会为模块启用一组更激进的优化；你应当对结果进行基准测试以确定这些优化是否确实能使代码运行更快速。 （由 Fredrik Lundh 在 NeedForSpeed sprint 中贡献。）

- `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`。

- 新增警告 `UnicodeWarning`，当尝试比较一个 Unicode 字符串和一个无法使用默认 ASCII 编码转换为 Unicode 的 8 位字符串时触发。之前此类比较会引发 `UnicodeDecodeError` 异常。

- 库：`csv` 模块现在对多行引用字段更为严格。如果你的文件中包含嵌入字段中的换行符，输入应按保留换行符的方式拆分为行。

- 库：`locale` 模块的 `format()` 函数之前会接受任何字符串，只要不超过一个 %char 指定符。在 Python 2.5 中，参数必须正好是一个 %char 指定符，且周围没有文本。

- 库：`pickle` 和 `cPickle` 模块不再接受 `__reduce__()` 方法返回值为 `None`；该方法必须返回一个参数元组。模块也不再接受已弃用的 bin 关键字参数。

- 库：`SimpleXMLRPCServer` 和 `DocXMLRPCServer` 类现在有一个 `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。