# Python 2.4 有什么新变化

作者:

  A.M. Kuchling

本文介绍了2005年3月30日发布的 Python 2.4.1 的新功能。

Python 2.4 是一个中等规模的发布版。 它引入的变化没有激进的 Python 2.2 那么多，但比保守的 2.3 发布版引入了更多的特性。 最主要的新语言特性是函数装饰器和生成器表达式；其他大部分改动都是针对标准库。

根据 CVS 变更日志，Python 2.3 和 2.4 之间共应用了 481 个补丁并修复了 502 个错误。 这两个数字可能都被低估了。

本文并不试图提供每一个新特性的完整规范说明，而是对每个特性进行简要的介绍。 要了解完整细节，你应该参考 Python 2.4 的文档，如 Python 库参考和 Python 参考手册等。 通常你需要参阅特定新特性的 PEP 以了解有关具体实现和设计原理的说明。

## PEP 218: 内置集合对象

Python 2.3 引入了 `sets` 模块。 现在集合数据类型的 C 语言实现作为两个新的内置类型 `set(iterable)` 和 `frozenset(iterable)` 被添加到 Python 内核中。 它们为成员测试、消除序列中的重复数据以及并集、交集、差集和对称差集等数学运算提供了高速操作。

    >>> a = set('abracadabra')              # 从字符串形成集合
    >>> 'z' in a                            # 快速成员测试
    False
    >>> a                                   # a中的唯一字母
    set(['a', 'r', 'b', 'c', 'd'])
    >>> ''.join(a)                          # 转换回字符串
    'arbcd'

    >>> b = set('alacazam')                 # 形成第二个集合
    >>> a - b                               # 在a中但不在b中的字母
    set(['r', 'd', 'b'])
    >>> a | b                               # 在a或b中的字母
    set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
    >>> a & b                               # 在a和b中的字母
    set(['a', 'c'])
    >>> a ^ b                               # 在a或b中但不在两者中的字母
    set(['r', 'd', 'b', 'm', 'z', 'l'])

    >>> a.add('z')                          # 添加新元素
    >>> a.update('wxy')                     # 添加多个新元素
    >>> a
    set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'x', 'z'])
    >>> a.remove('x')                       # 移除一个元素
    >>> a
    set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'z'])

`frozenset()` 类型是 `set()` 的不可变版本。 由于它是不可变且不可哈希的对象，因而可被用作字典的键或另一个集合的成员。

`sets` 模块仍被保留在标准库中，如果你想要子类化 `Set` 或 `ImmutableSet` 类时会很有用处。 目前还没有弃用该模块的计划。

参见

PEP 218 - 添加内置Set对象类型

  最初由 Greg Wilson 提出，由 Raymond Hettinger 最终实现。

## PEP 237: 统一长整数和整数

这个PEP的漫长过渡过程始于Python 2.2，在Python 2.4中又向前迈出了一步。在2.3中，某些在int/long统一后行为不同的整数操作会触发 `FutureWarning` 警告，并返回限于32位或64位（取决于您的平台）的值。在2.4中，这些表达式不再产生警告，而是产生一个通常为长整数的不同结果。

问题表达式主要是左移和长十六进制和八进制常量。例如，`2 << 32` 在2.3中会引发警告，在32位平台上评估为0。在Python 2.4中，这个表达式现在返回正确答案，8589934592。

参见

PEP 237 - 统一长整数和整数

  原始PEP由 Moshe Zadka 和 GvR 撰写，2.4 的变更由 Kalle Svensson 实现。

## PEP 289: 生成器表达式

Python 2.2中引入的迭代器特性和 `itertools` 模块使得编写循环遍历大数据集的程序变得更加容易，而无需将整个数据集一次性加载到内存中。列表推导式并不很适合这种场景，因为它们会产生一个包含所有项的Python列表对象。这不可避免地将所有对象拉入内存，如果数据集非常大，这可能会成为问题。当尝试编写函数式风格的程序时，自然会写出类似以下代码：

    links = [link for link in get_all_links() if not link.followed]
    for link in links:
        ...

代替：

    for link in get_all_links():
        if link.followed:
            continue
        ...

第一种形式更简洁，可能也更易读，但如果你正在处理大量的链接对象，为了避免同时将所有链接对象加载到内存中，你将不得不编写第二种形式。

生成器表达式的工作方式类似于列表推导式，但不生成整个列表；相反，它们创建一个生成器，逐个返回元素。上述示例可以改写为：

    links = (link for link in get_all_links() if not link.followed)
    for link in links:
        ...

生成器表达式必须像上述示例那样写在括号内。函数调用所用的括号也算在内，因此如果你想要创建一个将立即传递给函数的迭代器，可以写成：

    print sum(obj.count for obj in list_all_objects())

生成器表达式与列表推导式在各方面有细微的不同。最显著的是，循环变量（上述示例中的*obj*）在生成器表达式外部是不可访问的。列表推导式会将变量赋值为最后一个值；Python的未来版本将改变这一点，使列表推导式在这方面与生成器表达式一致。

参见

PEP 289 - 生成器表达式

  由Raymond Hettinger提出，Jiwon Seo实现，Hye-Shik Chang在早期进行了指导。

## PEP 292: 更简单的字符串替换

标准库中的一些新类提供了将变量替换到字符串中的替代机制；这种替换风格可能更适合那些未经训练的用户需要编辑模板的应用程序。

按名称替换变量的常用方式是 `%` 运算符:

    >>> '%(page)i: %(title)s' % {'page':2, 'title': 'The Best of Times'}
    '2: The Best of Times'

在编写模板字符串时，很容易忘记在闭合括号后加上 `i` 或 `s`。如果模板在 Python 模块中，这并不是一个大问题，因为你可以运行代码，得到一个“不支持的格式字符” `ValueError`，然后修复这个问题。然而，考虑像 Mailman 这样的应用程序，其中模板字符串或翻译是由不了解 Python 语言的用户编辑的。向这些用户解释格式字符串的语法是很复杂的，如果他们犯了错误，很难向他们提供有用的反馈。

PEP 292 给 `string` 模块增加了一个 `Template`，它使用 `$` 来表示替换:

    >>> import string
    >>> t = string.Template('$page: $title')
    >>> t.substitute({'page':2, 'title': 'The Best of Times'})
    '2: The Best of Times'

如果某个键在字典中找不到，`substitute()` 方法将引发 `KeyError`。 还有一个 `safe_substitute()` 方法则会忽略找不到的键:

    >>> t = string.Template('$page: $title')
    >>> t.safe_substitute({'page':3})
    '3: $title'

参见

PEP 292 - 更简单的字符串替换

  由 Barry Warsaw 撰写并实现。

## PEP 318: 函数和方法的装饰器

Python 2.2 通过添加静态方法和类方法扩展了 Python 的对象模型，但它并没有扩展 Python 的语法来提供任何定义静态方法或类方法的新方式。相反，你必须以通常的方式编写一个 `def` 语句，并将生成的函数传递给一个 `staticmethod()` 或 `classmethod()` 函数，该函数会将函数包装成新类型的方法。你的代码会像这样:

    class C:
       def meth (cls):
           ...

       meth = classmethod(meth)   # 将名称重新绑定到包装后的类方法

如果方法非常长，很容易错过或忘记在函数体后调用 `classmethod()`。

初衷一直是添加一些语法来使这样的定义更易读，但在 2.2 发布时，一个好的语法并不明显。今天，一个好的语法 仍然 不明显，但用户希望更方便地访问这个特性；为了满足这一需求，添加了一个新的语法特性。

这个新特性被称为“函数装饰器”。这个名字源于 `classmethod()`、`staticmethod()` 及其同类函数在函数对象上存储额外信息的想法；它们在用更多细节 装饰 函数。

这种表示法借鉴了 Java，并使用 `'@'` 字符作为指示符。使用新语法，上面的例子会写成:

    class C:

       @classmethod
       def meth (cls):
           ...

`@classmethod` 是 `meth=classmethod(meth)` 赋值的简写。更一般地，如果你有以下代码：

    @A
    @B
    @C
    def f ():
        ...

它等价于以下无装饰器的代码:

    def f(): ...
    f = A(B(C(f)))

装饰器必须放在函数定义的前一行，每行一个装饰器，不能与 def 语句在同一行，这意味着 `@A def f(): ...` 是非法的。你只能装饰模块级别或类内部的函数定义；不能装饰类定义。

装饰器只是一个函数，它接受要装饰的函数作为参数，并返回相同的函数或某个新对象。装饰器的返回值不一定是可调用的（尽管通常是这样），除非将进一步应用装饰器到结果上。编写自己的装饰器很容易。以下简单示例只是在函数对象上设置一个属性：

    >>> def deco(func):
    ...    func.attr = 'decorated'
    ...    return func
    ...
    >>> @deco
    ... def f(): pass
    ...
    >>> f
    <function f at 0x402ef0d4>
    >>> f.attr
    'decorated'
    >>>

作为一个稍微更现实的例子，以下装饰器检查提供的参数是否为整数：

    def require_int (func):
        def wrapper (arg):
            assert isinstance(arg, int)
            return func(arg)

        return wrapper

    @require_int
    def p1 (arg):
        print arg

    @require_int
    def p2(arg):
        print arg*2

在 PEP 318 中的一个示例包含了一个更复杂的版本，它允许你同时指定所需类型并检查返回类型。

装饰器函数可以接受参数。如果提供了参数，你的装饰器函数仅用这些参数调用，并且必须返回一个新的装饰器函数；这个函数必须接受单个函数并返回一个函数，如前所述。换句话说，`@A @B @C(args)` 变为：

    def f(): ...
    _deco = C(args)
    f = A(B(_deco(f)))

理解这一点可能有点费脑筋，但并不太困难。

一个小相关的更改使得函数的 `func_name` 属性可写。此属性用于在跟踪信息中显示函数名称，因此装饰器应更改任何新构造并返回的函数的名称。

参见

PEP 318 - 函数、方法和类的装饰器

  由 Kevin D. Smith、Jim Jewett 和 Skip Montanaro 编写。有几个人编写了实现函数装饰器的补丁，但实际被合并的是补丁 #979728，由 Mark Russell 编写。

https://wiki.python.org/moin/PythonDecoratorLibrary

  该Wiki页面包含几个装饰器示例。

## PEP 322: 反向迭代

一个新的内置函数 `reversed(seq)`，接受一个序列并返回一个以相反顺序遍历序列元素的迭代器。

    >>> for i in reversed(xrange(1,4)):
    ...    print i
    ...
    3
    2
    1

与扩展切片（如 `range(1,4)[::-1]`）相比，`reversed()` 更易读，运行更快，且显著减少内存使用。

请注意，`reversed()` 只接受序列，不接受任意迭代器。如果你想反转一个迭代器，首先使用 `list()` 将其转换为列表。

    >>> input = open('/etc/passwd', 'r')
    >>> for line in reversed(list(input)):
    ...   print line
    ...
    root:*:0:0:System Administrator:/var/root:/bin/tcsh
      ...

参见

PEP 322 - 反向迭代

  由 Raymond Hettinger 撰写并实现。

## PEP 324: 新的子进程模块

标准库提供了多种执行子进程的方式，提供不同的功能和不同的复杂度。`os.system(command)` 使用简单，但速度慢（它运行一个 shell 进程来执行命令）且存在风险（你必须小心处理 shell 的元字符转义）。`popen2` 模块提供可以捕获子进程标准输出和标准错误的类，但命名令人困惑。`subprocess` 模块对此进行了清理，提供了一个统一的接口，提供了你可能需要的所有功能。

与 `popen2` 的一系列类不同，`subprocess` 包含一个名为 `subprocess.Popen` 的单一类，其构造函数支持多种不同的关键字参数。

    class Popen(args, bufsize=0, executable=None,
                stdin=None, stdout=None, stderr=None,
                preexec_fn=None, close_fds=False, shell=False,
                cwd=None, env=None, universal_newlines=False,
                startupinfo=None, creationflags=0):

args 通常是一个字符串序列，用作子进程要执行的程序的参数。 (如果 shell 参数为真，args 可以是一个字符串，然后会传递给 shell 进行解释，就像 `os.system()` 所做的那样。)

stdin、stdout 和 stderr 指定子进程的输入、输出和错误流将会是什么。 你可以提供一个文件对象或文件描述符，或者你可以使用常量 `subprocess.PIPE` 来在子进程和父进程之间创建一个管道。

此构造器有几个方便的选项:

- close_fds 将在运行子进程之前请求关闭所有文件描述符。

- cwd 指定执行子进程将使用的工作目录（默认为父进程的工作目录）。

- env 是一个指定环境变量的字典。

- preexec_fn 是将在子进程启动之前被调用的函数。

- universal_newlines 使用 Python 的 universal newlines 特性打开子进程的输入和输出。

一旦你创建了 `Popen` 实例，你可以调用它的 `wait()` 方法来暂停直到子进程退出，`poll()` 来检查它是否已退出而不暂停，或者 `communicate(data)` 来将字符串 data 发送到子进程的标准输入。 `communicate(data)` 然后读取子进程发送到其标准输出或标准错误的任何数据，返回一个元组 `(stdout_data, stderr_data)`。

`call()` 是一个快捷方式，它将其参数传递给 `Popen` 构造函数，等待命令完成，并返回子进程的状态码。 它可以作为 `os.system()` 的更安全替代:

    sts = subprocess.call(['dpkg', '-i', '/tmp/new-package.deb'])
    if sts == 0:
        # 成功
        ...
    else:
        # dpkg 返回错误
        ...

命令在不使用 shell 的情况下被调用。 如果你确实想使用 shell，你可以添加 `shell=True` 作为关键字参数，并提供一个字符串而不是序列:

    sts = subprocess.call('dpkg -i /tmp/new-package.deb', shell=True)

PEP 提供了各种 shell 和 Python 代码示例，并展示了它们如何被翻译成使用 `subprocess` 的 Python 代码。强烈建议阅读 PEP 的这一部分。

参见

PEP 324 - 子进程 - 新的进程模块

  由 Peter Åstrand 在 Fredrik Lundh 等人的协助下撰写并实现。

## PEP 327: 十进制数据类型

Python 一直支持基于底层 C 语言 double 类型的浮点数（FP）作为数据类型。然而，尽管大多数编程语言提供了浮点数类型，但许多人（甚至程序员）并不知道浮点数不能准确表示某些十进制分数。新的 `Decimal` 类型可以准确表示这些分数，直至用户指定的精度限制。

### 为什么需要十进制？

这些限制源于浮点数的表示方式。FP 数由三个部分组成：

- 符号，可以是正或负。

- 尾数，是一个单数字的二进制数后跟一个小数部分。例如，二进制表示的 `1.01` 是 `1 + 0/2 + 1/4`，即十进制表示的 1.25。

- 指数，指示数字表示中十进制点的位置。

例如，数字 1.25 具有正符号，尾数值为 1.01（二进制），指数为 0（十进制点不需要移动）。数字 5 具有相同的符号和尾数，但指数为 2，因为尾数乘以 4（2 的指数 2 次方）；1.25 * 4 等于 5。

现代系统通常提供符合IEEE 754标准的浮点支持。C语言的 double 类型通常被实现为64位的IEEE 754数字，其中使用52位空间来存储尾数。这意味着数字只能精确到52位。如果你试图表示那些展开后会无限重复的数字，其展开会在52位后截断。不幸的是，大多数软件需要以十进制输出，而十进制中的常见分数在二进制中往往是无限循环小数。例如，十进制的1.1在二进制中是``1.0001100110011 ...``；0.1 = 1/16 + 1/32 + 1/256加上无限多个额外项。IEEE 754必须在52位后截断这个无限重复的小数，因此表示略有误差。

有时在打印数字时可以看到这种不准确性：

    >>> 1.1
    1.1000000000000001

这种不准确性在打印数字时不总是可见，因为浮点数到十进制字符串的转换由C库提供，大多数C库会尝试生成合理的输出。即使没有显示出来，不准确性仍然存在，后续操作可能会放大这个误差。

对于许多应用来说，这并不重要。如果我在绘制点并在显示器上显示它们，1.1和1.1000000000000001之间的差异太小，无法察觉。报告通常限制输出到一定的小数位数，如果你将数字四舍五入到两位、三位甚至八位小数，误差永远不会显现。然而，对于确实重要的应用，实现自己的自定义算术例程是一项大量工作。

因此，创建了 `Decimal` 类型。

### `Decimal` 类型

Python标准库中添加了一个新模块 `decimal` 。它包含两个类，`Decimal` 和 `Context`。`Decimal` 实例表示数字，而 `Context` 实例用于封装各种设置，例如精度和默认舍入模式。

`Decimal` 实例是不可变的，就像常规的 Python 整数和浮点数一样；一旦创建，就无法更改实例所表示的值。`Decimal` 实例可以从整数或字符串创建：

    >>> import decimal
    >>> decimal.Decimal(1972)
    Decimal("1972")
    >>> decimal.Decimal("1.1")
    Decimal("1.1")

你也可以提供包含符号、以十进制数字元组表示的尾数和指数的元组：

    >>> decimal.Decimal((1, (1, 4, 7, 5), -2))
    Decimal("-14.75")

注意事项：符号位是一个布尔值，所以 0 表示正数，1 表示负数。

从浮点数转换会带来一些问题：表示 1.1 的浮点数应该转换为精确的 1.1 的十进制数，还是 1.1 加上引入的任何不准确度？决定是回避这个问题，不将此类转换包含在 API 中。相反，你应该将浮点数转换为字符串，使用所需的精度，并将字符串传递给 `Decimal` 构造函数：

    >>> f = 1.1
    >>> decimal.Decimal(str(f))
    Decimal("1.1")
    >>> decimal.Decimal('%.12f' % f)
    Decimal("1.100000000000")

一旦你有了 `Decimal` 实例，你可以在它们上执行常规的数学运算。一个限制：指数运算需要整数指数：

    >>> a = decimal.Decimal('35.72')
    >>> b = decimal.Decimal('1.73')
    >>> a+b
    Decimal("37.45")
    >>> a-b
    Decimal("33.99")
    >>> a*b
    Decimal("61.7956")
    >>> a/b
    Decimal("20.64739884393063583815028902")
    >>> a ** 2
    Decimal("1275.9184")
    >>> a**b
    Traceback (most recent call last):
      ...
    decimal.InvalidOperation: x ** (非整数)

你可以将 `Decimal` 实例与整数组合，但不能与浮点数组合：

    >>> a + 4
    Decimal("39.72")
    >>> a + 4.5
    Traceback (most recent call last):
      ...
    TypeError: 你只能用 int, long 或 Decimal 数据类型与 Decimal 交互。
    >>>

`Decimal` 数字可以与 `math` 和 `cmath` 模块一起使用，但请注意，在进行操作之前，它们会立即转换为浮点数，这可能导致精度和准确性的损失。你也会得到一个普通的浮点数，而不是 `Decimal`。

    >>> import math, cmath
    >>> d = decimal.Decimal('123456789012.345')
    >>> math.sqrt(d)
    351364.18288201344
    >>> cmath.sqrt(-d)
    351364.18288201344j

`Decimal` 实例有一个 `sqrt()` 方法，返回一个 `Decimal`，但如果你需要其他函数，比如三角函数，你将不得不自己实现它们。

    >>> d.sqrt()
    Decimal("351364.1828820134592177245001")

### `Context` 类型

`Context` 类的实例封装了小数操作的几种设置：

- `prec` 是精度，即小数点后的位数。

- `rounding` 指定舍入模式。`decimal` 模块为各种可能性提供了常量：`ROUND_DOWN`、`ROUND_CEILING`、`ROUND_HALF_EVEN` 以及其他几种。

- `traps` 是一个字典，指定在遇到某些错误条件时会发生什么：要么抛出异常，要么返回一个值。一些错误条件的例子包括除以零、精度损失和溢出。

通过调用 `getcontext()` 可以获得线程本地的默认上下文；你可以更改此上下文的属性来改变默认的精度、舍入或错误处理。以下示例显示了更改默认上下文精度的效果:

    >>> decimal.getcontext().prec
    28
    >>> decimal.Decimal(1) / decimal.Decimal(7)
    Decimal("0.1428571428571428571428571429")
    >>> decimal.getcontext().prec = 9
    >>> decimal.Decimal(1) / decimal.Decimal(7)
    Decimal("0.142857143")

错误条件的默认行为是可选择的；模块可以返回特殊值，如无穷大或非数字，或者可以抛出异常:

    >>> decimal.Decimal(1) / decimal.Decimal(0)
    Traceback (most recent call last):
      ...
    decimal.DivisionByZero: x / 0
    >>> decimal.getcontext().traps[decimal.DivisionByZero] = False
    >>> decimal.Decimal(1) / decimal.Decimal(0)
    Decimal("Infinity")
    >>>

`Context` 实例还包含多种用于格式化数字的方法，例如 `to_eng_string()` 和 `to_sci_string()`。

更多信息请参阅 `decimal` 模块的文档，其中包含快速入门教程和参考手册。

参见

PEP 327 - 十进数据类型

  由 Facundo Batista 撰写，由Facundo Batista, Eric Price, Raymond Hettinger, Aahz 和 Tim Peters 实现。

http://www.lahey.com/float.htm

  该文章使用 Fortran 代码演示了浮点精度问题可能导致的许多问题。

https://speleotrove.com/decimal/

  描述了一种基于十进制的表示法。这种表示法已被提议作为标准，并且是新的 Python decimal 类型的基础。大部分内容由 Rexx 语言的设计者 Mike Cowlishaw 撰写。

## PEP 328: 多行导入

一个语言改动是针对从模块中导入多个名称的小语法调整。在 `from module import names` 语句中，names 是由逗号分隔的名称序列。如果序列非常长，您可以要么从同一模块多次导入，要么使用反斜杠转义行尾，如下所示:

    from SimpleXMLRPCServer import SimpleXMLRPCServer,\
                SimpleXMLRPCRequestHandler,\
                CGIXMLRPCRequestHandler,\
                resolve_dotted_attribute

Python 2.4 中的语法改动允许将名称放在括号内。Python 忽略括号表达式中的换行符，因此不再需要反斜杠:

    from SimpleXMLRPCServer import (SimpleXMLRPCServer,
                                    SimpleXMLRPCRequestHandler,
                                    CGIXMLRPCRequestHandler,
                                    resolve_dotted_attribute)

PEP还提议所有 `import` 语句应为绝对导入，使用前导的 `.` 字符表示相对导入。这部分PEP在Python 2.4中未实现，但在Python 2.5中完成了。

参见

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

  由 Aahz 撰写，多行导入由 Dima Dorfman 实现。

## PEP 331: 与区域设置无关的浮点数/字符串转换

`locale` 模块让Python软件选择针对特定国家或语言的多种转换和显示约定。然而，该模块小心翼翼地不更改数字区域设置，因为Python实现中的各种函数要求数字区域设置保持为 `'C'` 区域。这通常是因为代码使用了C库的 `atof()` 函数。

不设置数字区域设置给使用第三方C库的扩展带来了麻烦，因为它们不会有正确的区域设置。动机示例是GTK+，其用户界面小部件未在当前区域显示数字。

PEP中描述的解决方案是在Python API中添加三个新函数，执行仅ASCII的转换，忽略区域设置：

- `PyOS_ascii_strtod(str, ptr)` 和 `PyOS_ascii_atof(str, ptr)` 都将字符串转换为C double。

- `PyOS_ascii_formatd(buffer, buf_len, format, d)` 将 double 转换为ASCII字符串。

这些函数的代码来自GLib库（https://developer-old.gnome.org/glib/2.26/），其开发者友好地重新许可了相关函数并捐赠给Python软件基金会。`locale` 模块现在可以更改数字区域设置，让诸如GTK+之类的扩展产生正确的结果。

参见

PEP 331 - 与区域设置无关的浮点数/字符串转换

  由Christian R. Reis撰写，由 Gustavo Carneiro 实现。

## 其他语言特性修改

以下是 Python 2.4 针对核心 Python 语言的所有改变。

- 增加了用于函数和方法的装饰器 (PEP 318)。

- 内置的 `set()` 和 `frozenset()` 类型被添加（PEP 218）。其他新内置函数包括 `reversed(seq)` 函数（PEP 322）。

- 增加了生成器表达式 (PEP 289)。

- 某些数值表达式不再返回限于32位或64位的结果 (PEP 237)。

- 现在可以在 from module import names 语句中的名称列表周围加上括号 (PEP 328)。

- `dict.update()` 方法现在接受与 `dict` 构造函数相同的参数形式。这包括任何映射、任何键/值对的迭代器以及关键字参数。（由 Raymond Hettinger 贡献。）

- 字符串方法 `ljust()`、`rjust()` 和 `center()` 现在接受一个可选参数，用于指定除空格以外的填充字符。（由 Raymond Hettinger 贡献。）

- 字符串还增加了一个 `rsplit()` 方法，其工作方式类似于 `split()` 方法，但从字符串末尾开始分割。（由 Sean Reifschneider 贡献。）

      >>> 'www.python.org'.split('.', 1)
      ['www', 'python.org']
      'www.python.org'.rsplit('.', 1)
      ['www.python', 'org']

- 列表的 `sort()` 方法增加了三个关键字参数：cmp、key 和 reverse。这些参数使一些常见的 `sort()` 用法变得更简单。所有这些参数都是可选的。

  对于 cmp 参数，其值应为一个比较函数，该函数接受两个参数，根据参数的比较结果返回 -1、0 或 +1。然后使用此函数对列表进行排序。以前这是唯一可以提供给 `sort()` 的参数。

  key 应该是一个单参数函数，该函数接受一个列表元素并返回该元素的比较键。然后使用比较键对列表进行排序。以下示例按不区分大小写的方式对列表进行排序:

      >>> L = ['A', 'b', 'c', 'D']
      >>> L.sort()                 # 区分大小写的排序
      >>> L
      ['A', 'D', 'b', 'c']
      >>> # 使用 'key' 参数对列表进行排序
      >>> L.sort(key=lambda x: x.lower())
      >>> L
      ['A', 'b', 'c', 'D']
      >>> # 旧式方法
      >>> L.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
      >>> L
      ['A', 'b', 'c', 'D']

  最后一个示例使用了 cmp 参数，这是旧方法来进行不区分大小写的排序。它可以工作，但比使用 key 参数慢。使用 key 参数会对列表中的每个元素调用一次 `lower()` 方法，而使用 cmp 参数则会在每次比较时调用两次，因此使用 key 参数可以减少对 `lower()` 方法的调用。

  对于简单的键函数和比较函数，通常可以通过使用未绑定方法来避免使用 `lambda` 表达式。例如，上述不区分大小写的排序最好写成：

      >>> L.sort(key=str.lower)
      >>> L
      ['A', 'b', 'c', 'D']

  最后，reverse 参数接受一个布尔值。如果值为真，列表将按逆序排序。现在你可以用 `L.sort(reverse=True)` 替代 `L.sort(); L.reverse()`。

  排序结果现在保证是稳定的。这意味着具有相同键的两个条目将按照它们输入的顺序返回。例如，你可以先按名字对人员列表进行排序，然后再按年龄排序，结果是一个按年龄排序的列表，其中同年龄的人员按名字排序。

  （所有对 `sort()` 方法的更改由 Raymond Hettinger 贡献。）

- 有一个新的内置函数 `sorted(iterable)`，它类似于原地 `list.sort()` 方法，但可以在表达式中使用。区别在于：

- 输入可以是任意可迭代对象;

- 新生成的副本被排序，原始列表保持不变；以及

- 表达式返回新的已排序副本

      >>> L = [9,7,8,3,2,4,1,6,5]
      >>> [10+i for i in sorted(L)]       # 可用于列表推导
      [11, 12, 13, 14, 15, 16, 17, 18, 19]
      >>> L                               # 原列表保持不变
      [9,7,8,3,2,4,1,6,5]
      >>> sorted('Monty Python')          # 任何可迭代对象都可以作为输入
      [' ', 'M', 'P', 'h', 'n', 'n', 'o', 'o', 't', 't', 'y', 'y']

      >>> # 按键值排序并列出字典内容
      >>> colormap = dict(red=1, blue=2, green=3, black=4, yellow=5)
      >>> for k, v in sorted(colormap.iteritems()):
      ...     print k, v
      ...
      black 4
      blue 2
      green 3
      red 1
      yellow 5

  （由 Raymond Hettinger 贡献。）

- 整数运算将不再触发 `OverflowWarning`。`OverflowWarning` 警告将在 Python 2.5 中消失。

- 解释器新增了一个开关 `-m`，它接受一个名称，在 `sys.path` 上搜索相应的模块，并将该模块作为脚本运行。例如，你现在可以使用 `python -m profile` 运行 Python 分析器。（由 Nick Coghlan 贡献。）

- `eval(expr, globals, locals)` 和 `execfile(filename, globals, locals)` 函数以及 `exec` 语句现在接受任何映射类型作为 locals 参数。以前这必须是一个普通的 Python 字典。（由 Raymond Hettinger 贡献。）

- `zip()` 内置函数和 `itertools.izip()` 现在在无参数调用时返回一个空列表。以前它们会引发 `TypeError` 异常。这使得它们更适合用于可变长度参数列表:

      >>> def transpose(array):
      ...    return zip(*array)
      ...
      >>> transpose([(1,2,3), (4,5,6)])
      [(1, 4), (2, 5), (3, 6)]
      >>> transpose([])
      []

  （由 Raymond Hettinger 贡献。）

- 在导入模块时遇到失败将不再在 `sys.modules` 中留下部分初始化的模块对象。留下的不完整模块对象会导致后续导入同一模块时误以为成功，进而引发混淆的错误。（由 Tim Peters 修复。）

- `None` 现在是一个常量；将一个新值绑定到名称 `None` 的代码现在会造成语法错误。 （由 Raymond Hettinger 贡献。）

### 性能优化

- 列表和元组切片的内部循环经过优化，现在运行速度大约提高了三分之一。字典的内部循环也进行了优化，使得 `keys()`、`values()`、`items()`、`iterkeys()`、`itervalues()` 和 `iteritems()` 的性能得到提升。（由 Raymond Hettinger 贡献。）

- 用于列表增长和缩小的机制进行了速度和空间效率的优化。由于更高效的代码路径和更少地使用底层系统 `realloc()`，向列表追加和弹出元素现在运行得更快。列表推导也从中受益。`list.extend()` 也进行了优化，不再将其参数转换为临时列表后再扩展基础列表。（由 Raymond Hettinger 贡献。）

- `list()`、`tuple()`、`map()`、`filter()` 和 `zip()` 在使用提供 `__len__()` 方法的非序列参数时，现在运行速度提高了数倍。（由 Raymond Hettinger 贡献。）

- `list.__getitem__()`、`dict.__getitem__()` 和 `dict.__contains__()` 方法现在实现为 `method_descriptor` 对象，而不是 `wrapper_descriptor` 对象。这种访问形式使它们的性能翻倍，更适合作为函数式编程的参数使用：`map(mydict.__getitem__, keylist)`。（由 Raymond Hettinger 贡献。）

- 添加了一个新的操作码 `LIST_APPEND`，简化了列表推导生成的字节码，并使其速度提高了大约三分之一。（由 Raymond Hettinger 贡献。）

- 窥孔字节码优化器得到了改进，能够生成更短、更快的字节码；值得注意的是，生成的字节码更易读。（由 Raymond Hettinger 增强。）

- 在形式为 `s = s + "abc"` 和 `s += "abc"` 的语句中，字符串连接现在在某些情况下会更高效地执行。这种优化不会出现在其他Python实现中，例如Jython，因此你不应依赖它；当你需要高效地连接大量字符串时，仍然推荐使用字符串的 `join()` 方法。（由Armin Rigo贡献。）

2.4版本优化的净结果是，Python 2.4运行pystone基准测试比Python 2.3快约5%，比Python 2.2快35%。（pystone并不是一个特别好的基准测试，但它是衡量Python性能最常用的方法。你的应用程序可能从Python 2.4中获得更大或更小的收益。）

## 新增，改进和弃用的模块

一如既往，Python的标准库获得了一些增强和错误修复。以下是一些最显著变化的部分列表，按模块名称字母顺序排序。请查阅源树中的 `Misc/NEWS` 文件以获取更完整的变化列表，或通过CVS日志查看所有细节。

- `asyncore` 模块的 `loop()` 函数现在有一个 count 参数，允许你执行有限次数的轮询循环。默认情况下仍然是无限循环。

- `base64` 模块现在更完整地支持 RFC 3548 中的Base64、Base32和Base16编码和解码，包括可选的大小写折叠和可选的替代字母表。（由Barry Warsaw贡献。）

- `bisect` 模块现在有底层的C实现，以提升性能。（由Dmitry Vasiliev贡献。）

- 由 Hye-Shik Chang 维护的东亚编解码器的 CJKCodecs 集合已集成到 2.4 中。新的编码为：

- 汉语（大陆）: gb2312, gbk, gb18030, big5hkscs, hz

- 汉语（台湾）: big5, cp950

- 日语: cp932, euc-jis-2004, euc-jp, euc-jisx0213, iso-2022-jp,

    iso-2022-jp-1, iso-2022-jp-2, iso-2022-jp-3, iso-2022-jp-ext, iso-2022-jp-2004, shift-jis, shift-jisx0213, shift-jis-2004

- 韩语: cp949, euc-kr, johab, iso-2022-kr

- 添加了其他一些新的编码：HP Roman8、 ISO_8859-11、 ISO_8859-16、 PCTP-154 和 TIS-620。

- UTF-8和UTF-16编解码器现在更好地处理接收部分输入的情况。以前，`StreamReader` 类会尝试读取更多数据，使得无法从流中恢复解码。`read()` 方法现在将返回尽可能多的数据，后续调用将从之前停止的地方恢复解码。（由Walter Dörwald实现。）

- 有一个新的 `collections` 模块，用于各种专业化的集合数据类型。目前它只包含一种类型，`deque`，这是一个双端队列，支持高效地从两端添加和移除元素:

      >>> from collections import deque
      >>> d = deque('ghi')        # 创建一个新的包含三个项目的 deque
      >>> d.append('j')           # 在右侧添加一个新的条目
      >>> d.appendleft('f')       # 在左侧添加一个新的条目
      >>> d                       # 显示 deque 的表示
      deque(['f', 'g', 'h', 'i', 'j'])
      >>> d.pop()                 # 返回并移除最右侧的条目
      'j'
      >>> d.popleft()             # 返回并移除最左侧的条目
      'f'
      >>> list(d)                 # 列出 deque 的内容
      ['g', 'h', 'i']
      >>> 'h' in d                # 搜索 deque
      True

  多个模块，如 `Queue` 和 `threading` 模块，现在利用 `collections.deque` 来提高性能。（由 Raymond Hettinger 贡献。）

- `ConfigParser` 类略有增强。`read()` 方法现在返回成功解析的文件列表，如果传递给 `set()` 方法的 value 参数不是字符串，则会引发 `TypeError`。（由 John Belmonte 和 David Goodger 贡献。）

- `curses` 模块现在支持 ncurses 扩展 `use_default_colors()`。在终端支持透明度的平台上，这使得使用透明背景成为可能。（由 Jörg Lehmann 贡献。）

- `difflib` 模块现在包含一个 `HtmlDiff` 类，该类创建一个 HTML 表格，显示两个文本版本的并列比较。（由 Dan Gass 贡献。）

- `email` 包已更新至 3.0 版本，该版本移除了多种已弃用的 API，并取消了对于 Python 2.3 之前版本的支持。3.0 版本的包使用了一个新的增量解析器来处理 MIME 消息，该解析器位于 `email.FeedParser` 模块中。新解析器不需要将整个消息读入内存，且在消息格式不正确时不会抛出异常；相反，它会将任何问题记录在消息的 `defect` 属性中。（由 Anthony Baxter、Barry Warsaw、Thomas Wouters 等人开发。）

- `heapq` 模块已被转换为 C 语言实现。由此带来的速度提升达十倍，使得该模块适合处理大量数据。此外，该模块新增了两个函数 `nlargest()` 和 `nsmallest()`，它们使用堆来查找数据集中的 N 个最大或最小值，而无需进行完整的排序。（由 Raymond Hettinger 贡献。）

- `httplib` 模块现在包含了在多个与 HTTP 相关的 RFC 文档中定义的 HTTP 状态码常量。常量名称如 `OK`、`CREATED`、`CONTINUE` 和 `MOVED_PERMANENTLY`；使用 pydoc 可获取完整列表。（由 Andrew Eland 贡献。）

- `imaplib` 模块现在支持 IMAP 的 THREAD 命令（由 Yves Dionne 贡献）以及新的 `deleteacl()` 和 `myrights()` 方法（由 Arnaud Mazin 贡献）。

- `itertools` 模块新增了 `groupby(iterable[, *func*])` 函数。iterable 是可以迭代以返回元素流的对象，可选的 func 参数是一个函数，它接受一个元素并返回一个键值；如果省略，键即为元素本身。`groupby()` 然后将元素分组为具有匹配键值的子序列，并返回一系列包含键值和子序列迭代器的 2 元组。

  以下是一个示例，以便更清晰地说明这一点。key 函数 simply 返回一个数字是奇数还是偶数，因此 `groupby()` 的结果就是返回连续的奇数或偶数序列。:

      >>> import itertools
      >>> L = [2, 4, 6, 7, 8, 9, 11, 12, 14]
      >>> for key_val, it in itertools.groupby(L, lambda x: x % 2):
      ...    print key_val, list(it)
      ...
      0 [2, 4, 6]
      1 [7]
      0 [8]
      1 [9, 11]
      0 [12, 14]
      >>>

  `groupby()` 通常用于处理已排序的输入。`groupby()` 的逻辑类似于 Unix 的 `uniq` 过滤器，这使得它非常适合用于消除、计数或识别重复元素:

      >>> word = 'abracadabra'
      >>> letters = sorted(word)   # 将字符串转换为按字母排序的列表
      >>> letters
      ['a', 'a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'r', 'r']
      >>> for k, g in itertools.groupby(letters):
      ...    print k, list(g)
      ...
      a ['a', 'a', 'a', 'a', 'a']
      b ['b', 'b']
      c ['c']
      d ['d']
      r ['r', 'r']
      >>> # 列出唯一字母
      >>> [k for k, g in groupby(letters)]
      ['a', 'b', 'c', 'd', 'r']
      >>> # 计算字母出现次数
      >>> [(k, len(list(g))) for k, g in groupby(letters)]
      [('a', 5), ('b', 2), ('c', 1), ('d', 1), ('r', 2)]

  （由 Hye-Shik Chang 贡献。）

- `itertools` 还增加了一个名为 `tee(iterator, N)` 的函数，它返回 N 个独立迭代器，这些迭代器复制 iterator。如果省略 N，默认值为 2。:

      >>> L = [1,2,3]
      >>> i1, i2 = itertools.tee(L)
      >>> i1,i2
      (<itertools.tee object at 0x402c2080>, <itertools.tee object at 0x402c2090>)
      >>> list(i1)               # 运行第一个迭代器直到耗尽
      [1, 2, 3]
      >>> list(i2)               # 运行第二个迭代器直到耗尽
      [1, 2, 3]

  请注意，`tee()` 需要保留由迭代器返回的值的副本；在最坏的情况下，它可能需要保留所有这些值。因此，如果领先迭代器在长输入流中远远领先于滞后迭代器，应谨慎使用此功能。如果分离较大，那么您可能还不如使用 `list()`。当迭代器彼此紧密跟踪时，`tee()` 是理想的。可能的用例包括书签、窗口化或前瞻迭代器。（由 Raymond Hettinger 贡献。）

- `locale` 模块添加了一些函数，例如 `bind_textdomain_codeset()` 用于指定特定编码，以及一系列 `l*gettext()` 函数，这些函数返回所选编码中的消息。（由 Gustavo Niemeyer 贡献。）

- `logging` 包的 `basicConfig()` 函数添加了一些关键字参数，以简化日志配置。默认行为是将消息记录到标准错误，但可以指定各种关键字参数以将日志记录到特定文件、更改日志格式或设置日志级别。例如：

      import logging
      logging.basicConfig(filename='/var/log/application.log',
          level=0,  # 记录所有消息
          format='%(levelname):%(process):%(thread):%(message)')

  `logging` 包的其他添加内容包括一个 `log(level, msg)` 便捷方法，以及一个 `TimedRotatingFileHandler` 类，该类在定时间隔旋转其日志文件。模块已经有一个 `RotatingFileHandler`，它在文件超过特定大小时旋转日志。这两个类都派生自一个新的 `BaseRotatingHandler` 类，该类可用于实现其他旋转处理程序。

  （更改由 Vinay Sajip 实现。）

- `marshal` 模块现在在解包数据结构时共享内部字符串。这可能会缩小某些 pickle 字符串的大小，但主要效果是使 `.pyc` 文件显著变小。（由 Martin von Löwis 贡献。）

- `nntplib` 模块的 `NNTP` 类新增了 `description()` 和 `descriptions()` 方法，用于检索单个新闻组或一系列新闻组的描述。（由 Jürgen A. Erhard 贡献。）

- `operator` 模块新增了两个函数，`attrgetter(attr)` 和 `itemgetter(index)`。这两个函数都返回一个接受单个参数并返回相应属性或项的可调用对象；这些可调用对象在与 `map()` 或 `sorted()` 一起使用时，是非常优秀的数据提取器。例如：

      >>> L = [('c', 2), ('d', 1), ('a', 4), ('b', 3)]
      >>> map(operator.itemgetter(0), L)
      ['c', 'd', 'a', 'b']
      >>> map(operator.itemgetter(1), L)
      [2, 1, 4, 3]
      >>> sorted(L, key=operator.itemgetter(1)) # 按元组的第二个项对列表进行排序
      [('d', 1), ('c', 2), ('b', 3), ('a', 4)]

  （由 Raymond Hettinger 贡献。）

- `optparse` 模块进行了多项更新。该模块现在通过 `gettext.gettext()` 传递其消息，使得可以国际化 Optik 的帮助和错误消息。选项的帮助消息现在可以包含字符串 `'%default'`，该字符串将被选项的默认值替换。（由 Greg Ward 贡献。）

- 长期计划是在未来的某个 Python 版本中弃用 `rfc822` 模块，转而使用 `email` 包。为此，`email.Utils.formatdate` 函数已被修改，使其可以作为 `rfc822.formatdate()` 的替代。您在编写新的电子邮件处理代码时可能需要考虑这一点。（由 Anthony Baxter 实施。）

- `os` 模块新增了 `urandom(n)` 函数，返回一个包含 n 字节随机数据的字符串。该函数提供了对平台特定随机源（如 Linux 上的 `/dev/urandom` 或 Windows CryptoAPI）的访问。（由 Trevor Perrin 贡献。）

- 另一个新函数：`os.path.lexists(path)` 如果指定的文件存在（无论是否为符号链接）则返回 true。这与现有的 `os.path.exists(path)` 函数不同，后者在 path 是指向不存在目标的符号链接时返回 false。（由 Beni Cherniavsky 贡献。）

- 在 `os` 之下 `posix` 模块中新增了一个 `getsid()` 函数。 （由 J. Raynor 贡献。）

- `poplib` 模块现在已支持 SSL 上的 POP。 （由 Hector Urtubia 贡献。）

- 现在 `profile` 模块将可对 C 扩展函数执行性能分析。 （由 Nick Bastin 贡献。）

- `random` 模块有一个新方法 `getrandbits(N)`，返回一个长度为 N 位的长整数。现有的 `randrange()` 方法现在在适当的情况下使用 `getrandbits()`，使得生成任意大的随机数更加高效。（由 Raymond Hettinger 贡献。）

- `re` 模块接受的正则表达式语言扩展了简单的条件表达式，写作 `(?(group)A|B)`。group 可以是数字组 ID 或用 `(?P<group>...)` 在表达式中提前定义的组名。如果指定的组匹配，正则表达式模式 A 将被用于测试字符串；如果组未匹配，则使用模式 B。（由 Gustavo Niemeyer 贡献。）

- 由于 Gustavo Niemeyer 大量工作，`re` 模块也不再是递归的。在递归正则表达式引擎中，某些模式会导致大量 C 栈空间被消耗，甚至可能溢出栈。例如，如果你将一个 30000 字节的 `a` 字符串与表达式 `(a|b)+` 匹配，每个字符会消耗一个栈帧。Python 2.3 试图检查栈溢出并抛出 `RuntimeError` 异常，但某些模式可以绕过检查，如果你运气不好，Python 可能会段错误。Python 2.4 的正则表达式引擎可以无问题地匹配此模式。

- `signal` 模块现在对 `signal.signal()` 函数的参数进行更严格的错误检查。例如，你不能在 `SIGKILL` 信号上设置处理程序；之前的 Python 版本会默默接受这一点，但 2.4 版本将引发一个 `RuntimeError` 异常。

- `socket` 模块新增了两个函数。`socketpair()` 返回一对已连接的套接字，而 `getservbyport(port)` 用于查找给定端口号的服务名称。（由 Dave Cole 和 Barry Warsaw 贡献。）

- `sys.exitfunc()` 函数已被弃用。代码应使用现有的 `atexit` 模块，它能正确处理调用多个退出函数。最终 `sys.exitfunc()` 将成为一个纯内部接口，仅由 `atexit` 访问。

- 现在 `tarfile` 模块默认将生成 GNU 格式的 tar 文件。 （由 Lars Gustäbel 贡献。）

- `threading` 模块现在有了一种优雅简洁的方式来支持线程本地数据。该模块包含一个 `local` 类，其属性值对不同线程是本地的。:

      import threading

      data = threading.local()
      data.number = 42
      data.url = ('www.python.org', 80)

  其他线程可以为 `number` 和 `url` 属性分配和检索它们自己的值。你可以子类化 `local` 来初始化属性或添加方法。（由 Jim Fulton 贡献。）

- `timeit` 模块现在在计时循环中自动禁用周期性垃圾收集。这一改变使得连续计时更具可比性。（由 Raymond Hettinger 贡献。）

- `weakref` 模块现在支持更多种类的对象，包括 Python 函数、类实例、集合、冻结集合、双端队列、数组、文件、套接字和正则表达式模式对象。（由 Raymond Hettinger 贡献。）

- `xmlrpclib` 模块现在支持多调用扩展，用于在单个 HTTP 操作中传输多个 XML-RPC 调用。（由 Brian Quinlan 贡献。）

- `mpz`, `rotor` 和 `xreadlines` 模块已被移除。

### cookielib

`cookielib` 库支持客户端处理 HTTP Cookie，与 `Cookie` 模块的服務器端 Cookie 支持相呼应。Cookie 存储在 Cookie 罐中；该库透明地将网页服务器提供的 Cookie 存储在 Cookie 罐中，并在连接到服务器时从罐中提取 Cookie。与网页浏览器一样，策略对象控制是否接受 Cookie。

为了跨会话存储 Cookie，提供了两种 Cookie 罐的实现：一种以 Netscape 格式存储 Cookie，以便应用程序可以使用 Mozilla 或 Lynx 的 Cookie 文件，另一种以与 Perl libwww 库相同的格式存储 Cookie。

`urllib2` 已更改以与 `cookielib` 交互：`HTTPCookieProcessor` 管理一个在访问 URL 时使用的 Cookie 罐。

该模块由 John J. Lee 贡献。

### doctest

由于 Edward Loper 和 Tim Peters 的贡献，`doctest` 模块进行了大量重构。测试仍然可以简单到只需运行 `doctest.testmod()`，但重构允许以各种方式自定义模块的操作。

新的 `DocTestFinder` 类从给定对象的文档字符串中提取测试：

    def f (x, y):
        """>>> f(2,2)
    4
    >>> f(3,2)
    6
        """
        return x*y

    finder = doctest.DocTestFinder()

    # 获取 DocTest 实例列表
    tests = finder.find(f)

新的 `DocTestRunner` 类然后运行单个测试并可以生成结果摘要：

    runner = doctest.DocTestRunner()
    for t in tests:
        tried, failed = runner.run(t)

    runner.summarize(verbose=1)

上述示例生成以下输出：

    1 items passed all tests:
       2 tests in f
    2 tests in 1 items.
    2 passed and 0 failed.
    Test passed.

`DocTestRunner` 使用一个 `OutputChecker` 类的实例来比较预期输出和实际输出。这个类接受多个不同的标志来定制其行为；有野心的用户也可以编写一个全新的 `OutputChecker` 子类。

默认的输出检查器提供了一些方便的功能。例如，使用 `doctest.ELLIPSIS` 选项标志，预期输出中的省略号（`...`）可以匹配任何子字符串，这使得更容易适应在细小方面有所变化的输出:

    def o (n):
        """>>> o(1)
    <__main__.C instance at 0x...>
    >>>
    """

另一个特殊字符串，`<BLANKLINE>`, 匹配一个空行:

    def p (n):
        """>>> p(1)
    <BLANKLINE>
    >>>
    """

另一个新功能是通过指定 `doctest.REPORT_UDIFF` (统一差异) 、`doctest.REPORT_CDIFF` (上下文差异) 或 `doctest.REPORT_NDIFF` (增量式差异) 选项标志，生成输出差异风格的显示。例如:

    def g (n):
        """>>> g(4)
    here
    is
    a
    lengthy
    >>>"""
        L = 'here is a rather lengthy list of words'.split()
        for word in L[:n]:
            print word

在指定 `doctest.REPORT_UDIFF` 的情况下运行上述函数的测试，你会得到以下输出：

    **********************************************************************
    File "t.py", line 15, in g
    Failed example:
        g(4)
    Differences (unified diff with -expected +actual):
        @@ -2,3 +2,3 @@
         is
         a
        -lengthy
        +rather
    **********************************************************************

## 构建和 C API 的改变

对于 Python 构建过程和 C API 的一些修改:

- 为常见扩展函数返回值添加了三个新的便利宏：`Py_RETURN_NONE`、`Py_RETURN_TRUE` 和 `Py_RETURN_FALSE`。（由 Brett Cannon 贡献。）

- 另一个新宏，`Py_CLEAR`，减少 obj 的引用计数并将 obj 设置为空指针。（由 Jim Fulton 贡献。)

- 一个新的函数 `PyTuple_Pack(N, obj1, obj2, ..., objN)`，可以从一个可变长度的Python对象参数列表中构造元组。（由Raymond Hettinger贡献。）

- 一个新的函数 `PyDict_Contains(d, k)`，实现了快速的字典查找，而不会掩盖查找过程中引发的异常。（由Raymond Hettinger贡献。）

- 宏 Py_IS_NAN(X) 如果其浮点或双精度参数 X 是NaN，则返回1。（由Tim Peters贡献。）

- C代码可以通过使用新的 `PyEval_ThreadsInitialized()` 函数来避免不必要的锁定，以判断是否进行了任何线程操作。如果此函数返回false，则不需要锁定操作。（由Nick Coghlan贡献。）

- 一个新的函数 `PyArg_VaParseTupleAndKeywords()`，与 `PyArg_ParseTupleAndKeywords()` 相同，但接受一个 `va_list` 而不是参数数量。（由Greg Chapman贡献。）

- 一个新的方法标志 `METH_COEXIST`，允许在槽中定义的函数与具有相同名称的 `PyCFunction` 共存。这可以将诸如 `set.__contains__()` 这样的方法的访问时间减半。（由Raymond Hettinger贡献。）

- Python现在可以内置对解释器本身的额外分析功能，旨在帮助开发Python核心的人员。向 configure 脚本提供 `--enable-profiling` 选项将允许您使用 gprof 对解释器进行分析，提供 `--with-tsc` 开关则启用使用Pentium的时间戳计数器寄存器进行分析。请注意，`--with-tsc` 开关命名略有不当，因为该分析功能在PowerPC平台上也能工作，尽管该处理器架构不将该寄存器称为“TSC寄存器”。（由Jeremy Hylton贡献。）

- `tracebackobject` 类型已被重命名为 `PyTracebackObject`。

### 移植专属的改变

- Windows端口现在可以在MSVC++ 7.1以及版本6下构建。（由Martin von Löwis贡献。）

## 移植到 Python 2.4

本节列出了先前描述的可能需要修改你的代码的改变：

- 左移操作和过大的十六进制/八进制常量不再触发 `FutureWarning`，并且返回一个限制在32或64位的值；相反，它们返回一个长整数。

- 整数运算将不再触发 `OverflowWarning`。`OverflowWarning` 警告将在 Python 2.5 中消失。

- `zip()` 内置函数和 `itertools.izip()` 现在在无参数调用时返回一个空列表，而不是抛出 `TypeError` 异常。

- 您不能再比较由 `datetime` 模块提供的 `date` 和 `datetime` 实例。不同类的两个实例现在将始终不相等，并且相对比较（`<`，`>`）将抛出 `TypeError`。

- `dircache.listdir()` 现在将异常传递给调用者，而不是返回空列表。

- `LexicalHandler.startDTD()` 过去接收公共和系统 ID 的顺序错误。这已被纠正；依赖错误顺序的应用程序需要修复。

- 现在 `fcntl.ioctl()` 会在 mutate 参数被省略并且将造成影响时发出警告。

- 现在 `tarfile` 模块默认将生成 GNU 格式的 tar 文件。

- 在导入模块时遭遇失败不会再将部分初始化的模块对象留在 `sys.modules` 中。

- 现在 `None` 是一个常量；将一个新值绑定到 `None` 将导致语法错误。

- 现在对于某些非法的值 `signals.signal()` 函数将引发 `RuntimeError` 异常；在之前版本中这些错误会静默地放过。 例如，你将不能在 `SIGKILL` 信号上设置处理器。

## 致谢

作者感谢以下人员对本文各种草稿给予的建议、更正和协助：Koray Can、Hye-Shik Chang、Michael Dyck、Raymond Hettinger、Brian Hurt、Hamish Lawson、Fredrik Lundh、Sean Reifschneider、Sadruddin Rejeb。