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 语言的语法中要求括号，但出于风格考虑，我认为你应该总是使用它们
。考虑以下两个语句:

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

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

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

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

参见:

  **PEP 308** - 条件表达式
     PEP 由 Guido van Rossum 和 Raymond D 撰写，由 Thomas Wouters 实现
     。


PEP 309: 部分功能应用
=====================

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

这个模块中一个有用的工具是 "partial()" 函数。对于用函数式风格编写的程
序，有时你会想要构造一些现有函数的变体，其中部分参数已填充。考虑一个
Python函数``f(a, b, c)``；你可以创建一个新函数``g(b, c)``，它等同于
``f(1, b, c)``。这被称为“部分函数应用”。

"partial()" 接受参数 "(function, arg1, arg2, ... kwarg1=value1,
kwarg2=value2)"。生成的对象是可调用的，因此你可以直接调用它来使用填充
的参数调用*function*。

这里有一个很小但很现实的例子:

   import functools

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

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

这里还有一个例子，来自一个使用PyGTK的程序。这里正在动态构建一个上下文
敏感的弹出菜单。为菜单选项提供的回调是一个部分应用的 "open_item()" 方
法版本，其中第一个参数已经提供。

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

在 "functools" 模块中，另一个函数是 "update_wrapper(wrapper, wrapped)"
，它可以帮助你编写行为良好的装饰器。"update_wrapper()" 将名称、模块和
文档字符串属性复制到包装函数中，以便更容易理解包装函数内的追溯信息。例
如，你可能这样写:

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

"wraps()" 是一个装饰器，可以在你自己的装饰器中使用，以复制被包装函数的
信息。上一个示例的另一种版本可能是:

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

参见:

  **PEP 309** - 部分函数应用
     PEP由 Peter Harris 提出并撰写；由 Hye-Shik Chang 和 Nick Coghlan
     实现，并由 Raymond Hettinger 适配。


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

Distutils 中添加了一些简单的依赖支持。"setup()" 函数现在有了
"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" 形式时在模块名称前添加一个前导点，仍然可
以进行相对导入：

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

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

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

开头的句点不可用于 import 语句的 "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 value provided, change counter
           if val is not None:
               i = val
           else:
               i += 1

以下是更改计数器的示例:

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

"yield" 通常会返回 "None"，因此你应该总是检查这种情况。不要仅在表达式
中使用它的值，除非你确信 "send()" 方法将是唯一用于恢复生成器函数的方法
。

除了 "send()"，生成器上还有另外两个新方法：

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

* "close()" 方法在生成器内部引发一个新的 "GeneratorExit" 异常以终止迭
  代。接收到此异常时，生成器的代码必须要么引发 "GeneratorExit"，要么引
  发 "StopIteration"。捕获 "GeneratorExit" 异常并返回值是不合法的，将
  会触发 "RuntimeError"；如果函数引发其他异常，该异常将传播给调用者。
  "close()" 方法还将在生成器被垃圾回收时由 Python 的垃圾回收器调用。

  如果你需要在 "GeneratorExit" 发生的时候运行清理代码，我建议使用
  "try: ... finally:" 组合来代替捕获 "GeneratorExit"。

这些改变的累积效应是，让生成器从单向的信息生产者变成了既是生产者，又是
消费者。

生成器还可以成为 *协程*，这是一种更通用的子程序形式。子程序从一个点进
入并在另一个点退出（函数顶部和 "return" 语句），但协程可以在许多不同的
点进入、退出和恢复（"yield" 语句）。我们需要找出在 Python 中有效使用协
程的模式。

添加 "close()" 方法有一个不明显的影响。当生成器被垃圾回收时，会调用
"close()"，这意味着生成器的代码在生成器被销毁之前有最后一次运行的机会
。这最后一次机会意味着生成器中的 "try...finally" 语句现在可以保证工作
；"finally" 子句现在总能得到运行的机会。因此，之前的语法限制——不能将
"yield" 语句与 "try...finally" 块混合使用——已经被移除。这看起来像是语
言的小细节，但实际上使用生成器和 "try...finally" 对于实现 **PEP 343**
中描述的 "with" 语句是必要的。我将在下一节中探讨这个新语句。

这个变化的另一个更神秘的影响是：以前，生成器的 "gi_frame" 属性总是一个
帧对象。现在，一旦生成器耗尽，"gi_frame" 有可能变为 "None"。

参见:

  **PEP 342** - 通过增强型生成器实现协程
     PEP 由 Guido van Rossum 和 Phillip J. Eby 撰写，由 Phillip J. Eby
     实现。包括一些更高级的使用生成器作为协程的示例。

     这些功能的早期版本在 **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/~da
  n/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
           ... more processing code ...

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

备注:

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

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

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

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

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

   from decimal import Decimal, Context, localcontext

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

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


编写上下文管理器
----------------

在底层，'"with"' 语句相当复杂。大多数人只会与现有对象一起使用 '"with"'
，不需要了解这些细节，所以如果你愿意，可以跳过本节的其余部分。新对象的
作者需要理解底层实现的细节，应该继续阅读。

在更高层级上对于上下文管理器协议的解释:

* 表达式被评估，并应生成一个称为“上下文管理器”的对象。上下文管理器必须
  具有 "__enter__()" 和 "__exit__()" 方法。

* 调用上下文管理器的 "__enter__()" 方法。返回的值被赋给 *VAR*。如果没
  有 "'as VAR'" 子句，该值将被丢弃。

* *BLOCK* 中的代码会被执行。

* 如果 *BLOCK* 引发异常，将调用 "__exit__(type, value, traceback)" 方
  法，并传入异常详细信息，这些值与 "sys.exc_info()" 返回的值相同。该方
  法的返回值控制是否重新抛出异常：任何假值将重新抛出异常，而 "True" 将
  导致异常被抑制。通常情况下，你很少会想要抑制异常，因为如果你这么做，
  包含 '"with"' 语句的代码作者将永远不会意识到出了问题。

* 如果 *BLOCK* 没有引发异常，仍然会调用 "__exit__()" 方法，但 *type*、
  *value* 和 *traceback* 都是 "None"。

让我们通过一个例子来思考。我不会展示详细的代码，而只会概述支持事务的数
据库所需的方法。

（对于不熟悉数据库术语的人来说：对数据库的一组更改被组合成一个事务。事
务可以被提交，意味着所有更改都被写入数据库，或者被回滚，意味着所有更改
都被丢弃，数据库保持不变。更多信息请参阅任何数据库教材。）

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

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

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

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

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

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

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

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

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


contextlib 模块
---------------

新的 "contextlib" 模块提供了一些函数和一个装饰器，用于编写与 '"with"'
语句一起使用的对象。

该装饰器称为 "contextmanager()"，它允许你编写单个生成器函数，而不是定
义一个新的类。生成器应该产生恰好一个值。"yield" 之前的代码将作为
"__enter__()" 方法执行，产生的值将是该方法的返回值，该值将绑定到
'"with"' 语句的 "as" 子句中的变量（如果有的话）。"yield" 之后的代码将
在 "__exit__()" 方法中执行。块中引发的任何异常将由 "yield" 语句引发。

上一节中的数据库示例可以使用这个装饰器编写为:

   from contextlib import contextmanager

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

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

"contextlib" 模块还有一个``nested(mgr1, mgr2, ...)``函数，它可以将多个
上下文管理器组合在一起，这样你就不需要编写嵌套的'"with"'语句。在这个例
子中，单个'"with"'语句既启动数据库事务又获取线程锁:

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

最后，"closing(object)" 函数返回 *object*，以便它可以绑定到变量，并在
块的末尾调用 "object.close"。:

   import urllib, sys
   from contextlib import closing

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

参见:

  **PEP 343** - "with" 语句
     PEP由Guido van Rossum和Nick Coghlan撰写；由Mike Bland、Guido van
     Rossum和Neal Norwitz实现。PEP展示了为'"with"'语句生成的代码，这对
     于学习该语句的工作原理很有帮助。

  "contextlib" 模块的文档。


PEP 352: 异常作为新型的类
=========================

异常类现在可以是新式类，而不仅仅是经典类，内置的 "Exception" 类和所有
标准内置异常（"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:
       # Log error...
       # Continue running program...

在 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]   # Prints 1, 2
     print d[3], d[4]   # Prints 0, 0

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

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

  示例如下：

     >>> ('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']
     # Prints 'longest'
     print max(L, key=len)
     # Prints 'short', because lexicographically 'short' has the largest value
     print max(L)

  （由 Steven Bethard 和 Raymond Hettinger 贡献。）

* 两个新的内置函数，"any()" 和 "all()"，用于评估迭代器中是否包含任何真
  值或假值。"any()" 如果迭代器返回的任何值为真，则返回 "True"；否则返
  回 "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)   # Can't convert chr(128) to Unicode
     __main__:1: UnicodeWarning: Unicode equal comparison failed
       to convert both arguments to Unicode - interpreting them
       as being unequal
     False
     >>> chr(127) == unichr(127)   # chr(127) can be converted
     True

  以前这会引发一个 "UnicodeDecodeError" 异常，但在2.5版本中，这可能会
  导致在访问字典时出现令人困惑的问题。如果你查找 "unichr(128)" 而
  "chr(128)" 被用作键，你会得到一个 "UnicodeDecodeError" 异常。2.5版本
  中的其他更改导致这个异常被抛出，而不是被实现字典的 "dictobject.c" 中
  的代码抑制。

  对于这种比较引发异常是严格正确的，但这个更改可能会破坏代码，所以引入
  了 "UnicodeWarning"。

  （由 Marc-André Lemburg 实现。）

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

* 类定义中的基类列表现在可以是空的。例如，以下现在是合法的:

     class C():
         pass

  （由 Brett Cannon 实现。）


交互解释器变更
--------------

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

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

在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 日志查看所有详细信息。

* The "audioop" module now supports the a-LAW encoding, and the code
  for u-LAW encoding has been improved.  (Contributed by 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 模块 部分。

* 新模块："cProfile" 模块是现有 "profile" 模块的 C 语言实现，具有更低
  的开销。该模块的接口与 "profile" 相同：你可以通过运行
  "cProfile.run('main()')" 来分析函数，可以将分析数据保存到文件等。目
  前尚不清楚 Hotshot 分析器（同样用 C 语言编写，但接口与 "profile" 模
  块不匹配）是否会在 Python 的未来版本中继续维护。（由 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)  # Return two lowest elements, lexicographically
     ['longer still', 'longest']
     >>> heapq.nsmallest(2, L, key=len)   # Return two shortest elements
     ['short', 'medium']

  （由 Raymond Hettinger 贡献。）

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

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

  （由 Raymond Hettinger 贡献。）

* "locale" 模块中的 "format()" 函数已被修改，并添加了两个新函数：
  "format_string()" 和 "currency()"。

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

  要格式化带有多个 %char 指定符的字符串，请使用新的 "format_string()"
  函数，它类似于 "format()"，但还支持将 %char 指定符与任意文本混合。

  还添加了一个新的 "currency()" 函数，它根据当前区域设置的规则来格式化
  数字。

  （由Georg Brandl 贡献。）

* "mailbox" 模块进行了大规模重写，以增加修改邮箱的能力，而不仅仅是读取
  邮箱。一组新的类，包括 "mbox"、"MH" 和 "Maildir"，用于读取邮箱，并具
  有 "add(message)" 方法来添加消息，"remove(key)" 来删除消息，以及
  "lock()"/"unlock()" 来锁定/解锁邮箱。以下示例将 maildir 格式的邮箱转
  换为 mbox 格式的邮箱：

     import mailbox

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

     for msg in src:
         dest.add(msg)

  （由 Gregory K. Johnson 贡献。资金由 Google 2005 年夏季代码提供。）

* New module: the "msilib" module allows creating Microsoft Installer
  ".msi" files and CAB files.  Some support for reading the ".msi"
  database is also included. (Contributed by Martin von Löwis.)

* The "nis" module now supports accessing domains other than the
  system default domain by supplying a *domain* argument to the
  "nis.match()" and "nis.maps()" functions. (Contributed by 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()" 访问器
  方法，用于检索套接字的族、类型和协议值。

* New module: the "spwd" module provides functions for accessing the
  shadow password database on systems that support  shadow passwords.

* "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
     >>> # make a UUID based on the host ID and current time
     >>> uuid.uuid1()
     UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

     >>> # make a UUID using an MD5 hash of a namespace UUID and a name
     >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
     UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')

     >>> # make a random UUID
     >>> uuid.uuid4()
     UUID('16fd2706-8baf-433b-82eb-8c7fada847da')

     >>> # make a UUID using a SHA-1 hash of a namespace UUID and a name
     >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
     UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')

  （由 Ka-Ping Yee 贡献。）

* "weakref" 模块的 "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" also provides a wrapper for Python's C API  as the
"ctypes.pythonapi" object.  This object does *not*  release the global
interpreter lock before calling a function, because the lock must be
held when calling into the interpreter's code.   There's a
"py_object()" type constructor that will create a  PyObject* pointer.
A simple usage:

   import ctypes

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

Don't forget to use "py_object()"; if it's omitted you end  up with a
segmentation fault.

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

参见:

  https://web.archive.org/web/20180410025338/http://starship.python.n
  et/crew/theller/ctypes/
     pre-stdlib 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()" 类似
，它既可以接受一个字符串，也可以接受一个类似文件的对象：

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

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

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

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

参见:

  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 的包装器以保持向后兼容
性。新模块的接口与旧模块非常接近，但不完全相同。最显著的区别是用于创建
新哈希对象的构造函数命名不同。:

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

   # New version
   h = hashlib.md5()

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

   # New version
   h = hashlib.sha1()

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

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

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

参见: "hashlib" 模块的文档。


sqlite3 包
----------

pysqlite 模块 (https://www.pysqlite.org) 是 SQLite 嵌入式数据库的封装
器，已添加到标准库中，软件包名称为 "sqlite3"。

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

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

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

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

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

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

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

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

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

下面是一个使用迭代器形式的例子：

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

有关 SQLite 所支持的 SQL 方法的更多信息，请参阅 https://www.sqlite.org
。

参见:

  https://www.pysqlite.org
     pysqlite 的主页

  https://www.sqlite.org
     SQLite的主页；它的文档详细描述了它所支持的 SQL 方言的语法和可用的
     数据类型。

  "sqlite3" 模块的文档。

  **PEP 249** - 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)" 声明函数返回指定 *type* 类型的值并使用快速调用限定
  符。"Py_LOCAL_INLINE(type)" 执行相同操作并请求函数内联。如果在包含
  "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.
