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* 函数只是返回一个数字是
  奇数还是偶数，因此 "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。
