Python 2.5 有什么新变化

作者:

A.M. Kuchling

本文介绍了 Python 2.5 的新增特性。 Python 2.5 预定的最终发布时间为 2006 年 8 月;PEP 356 描述了预定的发布日程。 Python 2.5 实际发布于 2006 年 9 月 19 日。

Python 2.5 中的变化是语言本身和标准库改进的有趣混合。 我想,库的加强对 Python 的用户社区来说会更重要一些,因为增加了多个被广泛应用的包。 新增模块包括用于 XML 处理的 ElementTree (xml.etree),SQLite 数据库模块 (sqlite),以及用于调用 C 函数的 ctypes 模块。

语言变更具有中等重要性。一些令人愉悦的新特性被添加,但其中大部分并非每天都会使用的特性。条件表达式终于被添加到语言中,使用了新颖的语法;参见章节 PEP 308: 条件表达式。新的 'with' 语句将使编写清理代码变得更加容易(章节 PEP 343: "with" 语句)。现在可以将值传递给生成器(章节 PEP 342: 生成器的新特性)。导入现在可以显示为绝对或相对(章节 PEP 328: 绝对导入和相对导入)。一些异常处理的边缘情况得到了更好的处理(章节 PEP 341: 统一 try/except/finally)。所有这些改进都是有价值的,但它们都是对特定语言特性的改进;没有一个是对 Python 语义的广泛修改。

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

本文并不试图成为新特性的完整规范;相反,通过有用的示例简要介绍了这些变更。有关完整细节,你应始终参考 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_vif cond then true_v else false_v,以及其他16种变体。

Guido van Rossum 最终选择了一种令人惊讶的语法:

x = true_value if condition else false_value

求值过程仍保持与现有布尔表达式相同的惰性特性,因此求值顺序会有所调整。中间的 condition 表达式会首先被求值,而 true_value 表达式仅在条件为真时才会被求值。同理,false_value 表达式仅在条件为假时才会被求值。

这种语法可能看起来奇怪且不合常理;为什么条件要放在表达式的 中间,而不是像C语言的 c ? x : y 那样放在前面?这一决定是通过将新语法应用于标准库中的模块,并查看生成的代码的可读性来验证的。在许多使用条件表达式的情况下,一个值似乎是“常见情况”,而另一个值是“特殊情况”,仅在条件不满足的罕见情况下使用。条件语法使这种模式更加明显:

contents = ((doc + '\n') if doc else '')

我将上述声明理解为“通常 contents 被赋值为 doc+'\n';有时 doc 为空,在这种情况下返回空字符串。”我怀疑在不存在明确常见和罕见情况的地方,我很少会使用条件表达式。

曾经有一些讨论,关于语言是否应该要求在条件表达式周围加上括号。决定是不在Python语言的语法中要求括号,但出于风格考虑,我认为你应该总是使用它们。考虑以下两个语句:

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

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

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

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

参见

PEP 308 - 条件表达式

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

PEP 309: 部分函数应用

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

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

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

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

import functools

def log (message, subsystem):
    "将 'message' 的内容写到指定的子系统。"
    print '%s: %s' % (subsystem, message)
    ...

server_log = functools.partial(log, subsystem='server')
server_log('不能打开套接字')

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

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

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

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

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

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

参见

PEP 309 - 部分函数应用

PEP由 Peter Harris 提出并撰写;由 Hye-Shik Chang 和 Nick Coghlan 实现,并由 Raymond Hettinger 适配。

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

Distutils 中添加了一些简单的依赖支持。setup() 函数现在有了 requiresprovidesobsoletes 关键字参数。当你使用 sdist 命令构建源代码分发时,依赖信息将被记录在 PKG-INFO 文件中。

另一个新的关键字形参是 download_url,它应该被设置为包源代码的 URL。这意味着现在可以查找包索引中的条目,确定包的依赖项,并下载所需的包。:

VERSION = '1.0'
setup(name='PyPackage',
      version=VERSION,
      requires=['numarray', 'zlib (>=1.1.4)'],
      obsoletes=['OldPackage']
      download_url=('http://www.example.com/pypackage/dist/pkg-%s.tar.gz'
                    % VERSION),
     )

Python 包索引 https://pypi.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.mainpkg.string 子模块。

考虑 main.py 模块中的代码。如果它执行了语句 import string 会发生什么?在 Python 2.4 及更早版本中,它首先会在包的目录中查找以进行相对导入,找到 pkg/string.py,将该文件的内容导入为 pkg.string 模块,并将该模块绑定到 pkg.main 模块命名空间中的名称 string

如果这正是你想要的 pkg.string 模块,那自然没问题。但如果你实际需要的是 Python 标准库中的 string 模块呢?目前没有优雅的方式可以忽略 pkg.string 而直接查找标准模块——通常你不得不检查 sys.modules 的内容,这种做法稍显不够优雅。Holger Krekel 开发的 py.std 包提供了一种更整洁的标准库导入方式:import py; py.std.string.join(),但该包并非所有 Python 环境都默认安装。

阅读依赖相对导入的代码时清晰度也会降低,因为读者可能会困惑于实际使用的是哪个模块——是 string 还是 pkg.string。Python 用户很快意识到不应在包的子模块命名时重复标准库模块的名称,但你无法避免未来 Python 版本新增模块时恰好与你的子模块同名。

在 Python 2.5 中,你可以通过使用 from __future__ import absolute_import 指令将 import 的行为切换为绝对导入。这种绝对导入行为将在未来版本(可能是 Python 2.7)中成为默认行为。一旦绝对导入成为默认行为,import string 将始终找到标准库的版本。建议用户尽可能开始使用绝对导入,因此最好在代码中开始写 from pkg import string

通过在使用 from ... import 形式时在模块名称前添加一个前导句点,仍然可以进行相对导入:

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

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

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

开头的句点不可用于 import 语句的 import modname 形式,只能用于 from ... import 形式。

参见

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

PEP 由 Aahz 撰写,由 Thomas Wouters 实现。

https://pylib.readthedocs.io/

由 Holger Krekel 编写 py 库,其中包含 py.std 包。

PEP 338: 将模块作为脚本执行

Python 2.4 中添加的 -m 开关用于将模块作为脚本执行,获得了一些新能力。该开关现在不再使用 Python 解释器内部的 C 代码实现,而是使用一个新模块 runpy 中的实现。

runpy 模块实现了一个更复杂的导入机制,因此现在可以运行包中的模块,例如 pychecker.checker。该模块还支持其他导入机制,例如 zipimport 模块。这意味着你可以将 .zip 归档的路径添加到 sys.path 中,然后使用 -m 开关来执行归档中的代码。

参见

PEP 338 - 将模块作为脚本执行

PEP 由 Nick Coghlan 撰写并实现。

PEP 341: 统一 try/except/finally

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

Guido van Rossum 花了一些时间研究 Java,Java 支持相当于组合 except 块和 finally 块的功能,这澄清了该语句应该是什么意思。在 Python 2.5 中,你现在可以写:

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

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

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

参见

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

PEP 由 Georg Brandl 撰写,由 Thomas Lee 实现。

PEP 342: 生成器的新特性

Python 2.5 添加了一种简单的方法来将值 传入 生成器。如 Python 2.3 中所引入的,生成器仅产生输出;一旦生成器的代码被调用以创建迭代器,当其执行恢复时,无法将任何新信息传入函数。有时能够传入一些信息会很有用。对此的权宜之计包括让生成器的代码查看全局变量,然后更改全局变量的值,或者传入一些可变对象,然后调用者修改它。

为了刷新你对基本生成器的记忆,这里有一个简单的示例:

def counter (maximum):
    i = 0
    while i < maximum:
        yield i
        i += 1

当你调用 counter(10) 时,结果是返回从 0 到 9 的值的迭代器。在遇到 yield 语句时,迭代器返回提供的值并挂起函数的执行,保留局部变量。执行在下次调用迭代器的 next() 方法时恢复,从 yield 语句之后开始。

在 Python 2.3 中,yield 是一个语句;它不返回任何值。在 2.5 中,yield 现在是一个表达式,返回一个可以赋值给变量或进行其他操作的值:

val = (yield i)

我建议在处理返回值时,始终在 yield 表达式周围加上括号,如上例所示。虽然括号并非总是必要,但总是添加它们比记住何时需要它们更容易。

(PEP 342 解释了确切规则,即 yield-表达式必须始终加括号,除非它出现在赋值右侧的顶层表达式。这意味着你可以写 val = yield i,但在有操作时必须使用括号,如 val = (yield i) + 12。)

通过调用生成器的 send(value) 方法,可以将值发送到生成器中。然后生成器的代码会继续执行,yield 表达式返回指定的 value。如果调用常规的 next() 方法,yield 返回 None

以下是修改后的示例,允许更改内部计数器的值。

def counter (maximum):
    i = 0
    while i < maximum:
        val = (yield i)
        # 如果提供了值,更改计数器
        if val is not None:
            i = val
        else:
            i += 1

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

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

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

除了 send() 之外,生成器上还有两个新方法:

  • throw(type, value=None, traceback=None) 用于在生成器内部引发异常;异常由 yield 表达式在生成器执行暂停处引发。

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

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

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

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

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

这一变化的另一个更玄妙的影响:之前,生成器的 gi_frame 属性总是一个帧对象。现在,生成器耗尽后,gi_frame 有可能为 None

参见

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

PEP 由 Guido van Rossum 和 Phillip J. Eby 撰写,由 Phillip J. Eby 实现。包括一些更高级的使用生成器作为协程的示例。

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

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

协程的Wikipedia条目。

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

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

PEP 343: "with" 语句

'with' 语句澄清了之前使用 try...finally 块以确保执行清理代码的代码。在本节中,我将讨论该语句的常见用法。在下一节中,我将探讨实现细节,并展示如何编写与该语句一起使用的对象。

'with' 语句是一种新的控制流结构,其基本结构如下:

with expression [as variable]:
    with-block

表达式被求值,并且它应该生成一个支持上下文管理协议的对象(即具有 __enter__()__exit__() 方法的对象)。

在执行 with-block 之前,调用对象的 __enter__() 方法,因此可以运行设置代码。如果给定,它还可以返回一个绑定到 variable 名称的值。(请注意,variable 并不是被赋值为 expression 的结果。)

with-block 执行完成后,调用对象的 __exit__() 方法,即使块引发了异常,因此可以运行清理代码。

要在 Python 2.5 中启用该语句,你需要向你的模块添加以下指令:

from __future__ import with_statement

该语句在Python 2.6 中始终启用。

一些标准 Python 对象现在已支持上下文管理协议并可被用于 'with' 语句。 文件对象就是一个例子:

with open('/etc/passwd', 'r') as f:
    for line in f:
        print line
        ... 更多处理代码 ...

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

备注

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

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

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

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

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

from decimal import Decimal, Context, localcontext

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

with localcontext(Context(prec=16)):
    # 此块中的所有代码使用 16 位精度。
    # 退出块时恢复原始上下文。
    print v.sqrt()

编写上下文管理器

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

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

  • 表达式被求值,并应生成一个称为“上下文管理器”的对象。上下文管理器必须具有 __enter__()__exit__() 方法。

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

  • BLOCK 中的代码会被执行。

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

  • 如果 BLOCK 没有引发异常,仍会调用 __exit__() 方法,但 typevaluetraceback 都为 None

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

(对于不熟悉数据库术语的人:对数据库的一组更改被分组为一个事务。事务可以被提交,意味着所有更改都被写入数据库,或者回滚,意味着所有更改都被丢弃,数据库保持不变。更多信息请参见任何数据库教材。)

假设有一个表示数据库连接的对象。我们的目标将是可以让用户编写如下代码:

db_connection = DatabaseConnection()
with db_connection as cursor:
    cursor.execute('insert into ...')
    cursor.execute('delete from ...')
    # ... 更多操作 ...

如果块中的代码无异常运行,事务应被提交;如果出现异常,应回滚。以下是我假设的 DatabaseConnection 的基本接口:

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

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

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

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

在下面的代码中,执行将直接从函数末尾掉落,返回默认值 NoneNone 是假的,所以异常将自动重新抛出。如果你希望更明确,你可以在标记的位置添加一个 return 语句。

class DatabaseConnection:
    ...
    def __exit__ (self, type, value, tb):
        if tb is None:
            # 没有异常,因此提交
            self.commit()
        else:
            # 发生异常,因此回滚。
            self.rollback()
            # 返回 False

contextlib 模块

新的 contextlib 模块提供了一些函数和一个装饰器,这些对于编写用于 'with' 语句的对象非常有用。

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

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

from contextlib import contextmanager

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

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

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

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

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

import urllib, sys
from contextlib import closing

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

参见

PEP 343 - "with" 语句

PEP 由 Guido van Rossum 和 Nick Coghlan 撰写;由 Mike Bland、Guido van Rossum 和 Neal Norwitz 实现。PEP 展示了为 'with' 语句生成的代码,这有助于了解该语句的工作原理。

contextlib 模块的文档。

PEP 352: 异常作为新型的类

异常类现在可以是新式类,而不仅仅是经典类,内置的 Exception 类和所有标准内置异常(NameErrorValueError 等)现在都是新式类。

异常的继承层次结构已经稍作调整。在 2.5 中,继承关系如下:

BaseException       # Python 2.5 中新增
|- KeyboardInterrupt
|- SystemExit
|- Exception
   |- 所有其他当前内置异常

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

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

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

Python 3.0 的目标是要求任何作为异常被引发的类都必须派生自 BaseExceptionBaseException 的某个子类,未来 Python 2.x 系列的发布版可能会开始强制执行这一约束。 因此,我建议你现在就开始让你的所有异常类都从 Exception 派生。 有建议称裸露的 except: 形式应在 Python 3.0 中移除,但 Guido van Rossum 尚未决定是否要这样做。

在 Python 2.5 中,如 raise "Error occurred" 这样的将字符串作为异常抛出的做法已被弃用,并会触发警告。目标是能够在几个版本后移除字符串异常功能。

参见

PEP 352 - 异常所需的超类

PEP 由 Brett Cannon 和 Guido van Rossum 撰写,由 Brett Cannon 实现。

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

对 Python 的 C API 进行了广泛更改,使用新的 Py_ssize_t 类型定义代替 int,这将允许解释器在 64 位平台上处理更多数据。这一变化不影响 Python 在 32 位平台上的能力。

Python 解释器的各个部分使用 C 语言的 int 类型来存储大小或计数;例如,列表或元组中的项数就存储在 int 中。 大多数 64 位平台的 C 编译器仍然将 int 定义为 32 位类型,这意味着列表最多只能容纳 2**31 - 1 = 2147483647 项。 (实际上,64 位 C 编译器可以使用几种不同的编程模型 —— 参见 https://unix.org/version2/whatsnew/lp64_wp.html 进行讨论 —— 但最常用的模型将 int 保留为 32 位。)

在 32 位平台上,2147483647 项的限制实际上并不重要,因为在达到长度限制之前你就会耗尽内存。 每个列表项需要为指针预留空间,指针占 4 字节,再加上表示该项的 PyObject 所需的空间。 2147483647*4 已经超过了 32 位地址空间所能容纳的字节数。

然而,在 64 位平台上可以寻址那么多的内存。 那样大小的列表的指针只需要 16 GiB 的空间,因此 Python 程序员构建如此大的列表并非不合理。 因此,Python 解释器必须改用某种不同于 int 的类型,而在 64 位平台上这将是一个 64 位类型。这一更改会导致 64 位机器上的不兼容,因此被认为现在进行过渡是值得的,因为 64 位用户数量仍然相对较少。 (在 5 年或 10 年后,我们可能 在使用 64 位机器,那时过渡会更加痛苦。)

这一更改对 C 扩展模块的作者影响最大。 Python 字符串和容器类型(如列表和元组)现在使用 Py_ssize_t 来存储其大小。 诸如 PyList_Size() 之类的函数现在返回 Py_ssize_t。 因此,扩展模块中的代码可能需要将一些变量更改为 Py_ssize_t

PyArg_ParseTuple()Py_BuildValue() 函数新增了一个转换代码 n,用于 Py_ssize_tPyArg_ParseTuple()s#t# 默认仍输出 int,但你可以在包含 Python.h 之前定义宏 PY_SSIZE_T_CLEAN,以使它们返回 Py_ssize_t

PEP 353 有一个关于转换指南的章节,扩展作者应阅读以了解如何支持64位平台。

参见

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

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

PEP 357: '__index__' 方法

NumPy 开发者遇到了一个问题,只能通过添加一个新的特殊方法 __index__() 来解决。在使用切片表示法,如 [start:stop:step] 时,startstopstep 索引的值必须都是整数或长整数。NumPy 定义了多种专门的整数类型,对应于8、16、32和64位的无符号和有符号整数,但没有办法表示这些类型可以用作切片索引。

切片不能仅使用现有的 __int__() 方法,因为该方法还用于实现强制转换为整数。如果切片使用 __int__(),浮点数也会成为合法的切片索引,这显然是不希望的行为。

相反,添加了一个新的特殊方法 __index__()。它不接受任何参数,返回一个整数,给出要使用的切片索引。例如:

class C:
    def __index__ (self):
        return self.value

返回值必须是 Python 整数或长整数。解释器将检查返回的类型是否正确,如果不满足此要求,将引发 TypeError

在 C 级的 PyNumberMethods 结构中添加了相应的 nb_index 插槽,以便 C 扩展实现此协议。PyNumber_Index(obj) 可用于扩展代码中调用 __index__() 函数并获取其结果。

参见

PEP 357 - 允许将任何对象用于切片

PEP 由 Travis Oliphant 撰写并实现。

其他语言特性修改

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

  • dict 类型新增了一个钩子,允许子类在字典中不包含某个键时提供默认值。当找不到键时,将调用字典的 __missing__(key) 方法。这个钩子用于在 collections 模块中实现新的 defaultdict 类。以下示例定义了一个字典,对于任何缺失的键返回零:

    class zerodict (dict):
        def __missing__ (self, key):
            return 0
    
    d = zerodict({1:1, 2:2})
    print d[1], d[2]   # 输出 1, 2
    print d[3], d[4]   # 输出 0, 0
    
  • 8位字符串和Unicode字符串都新增了 partition(sep)rpartition(sep) 方法,简化了常见的用例。

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

    示例如下:

    >>> ('http://www.python.org').partition('://')
    ('http', '://', 'www.python.org')
    >>> ('file:/usr/share/doc/index.html').partition('://')
    ('file:/usr/share/doc/index.html', '', '')
    >>> (u'Subject: a quick question').partition(':')
    (u'Subject', u':', u' a quick question')
    >>> 'www.python.org'.rpartition('.')
    ('www.python', '.', 'org')
    >>> 'www.python.org'.rpartition(':')
    ('', '', 'www.python.org')
    

    (由 Fredrik Lundh 在 Raymond Hettinger 的建议下实现。)

  • 现在字符串类型的 startswith()endswith() 方法可接受字符串元组供检查。

    def is_image_file (filename):
        return filename.endswith(('.gif', '.jpg', '.tiff'))
    

    (由 Georg Brandl 实现,基于 Tom Lynn 的建议。)

  • min()max() 内置函数新增了一个 key 关键字参数,类似于 sort()key 参数。这个参数提供一个函数,该函数接受单个参数,并对列表中的每个值调用;min()/max() 将返回此函数返回值最小/最大的元素。例如,要找到列表中最长的字符串,可以这样做:

    L = ['medium', 'longest', 'short']
    # 输出 'longest'
    print max(L, key=len)
    # 输出 'short',因为在字典序上 'short' 具有最大值
    print max(L)
    

    (由 Steven Bethard 和 Raymond Hettinger 贡献。)

  • 两个新的内置函数,any()all(),用于求值迭代器中是否包含任何真值或假值。any() 如果迭代器返回的任何值为真,则返回 True;否则返回 Falseall() 只有当迭代器返回的所有值都为真时,才返回 True。(由 Guido van Rossum 建议,Raymond Hettinger 实现。)

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

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

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

    >>> chr(128) == unichr(128)   # 无法将 chr(128) 转换为 Unicode
    __main__:1: UnicodeWarning: Unicode 等值比较失败
      无法将两个参数都转换为 Unicode - 将它们解释为不等
    False
    >>> chr(127) == unichr(127)   # chr(127) 可以转换
    True
    

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

    对于这样的比较引发异常是严格正确的,但这个更改可能会破坏代码,因此引入了 UnicodeWarning

    (由 Marc-André Lemburg 实现。)

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

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

    class C():
        pass
    

    (由 Brett Cannon 实现。)

交互解释器变更

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

>>> quit
'使用 Ctrl-D(即 EOF)退出。'

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

Python可执行文件现在接受标准的长选项 --help--version;在Windows上,它还接受 /? 选项来显示帮助消息。(由Georg Brandl实现。)

性能优化

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

  • 当内置的 setfrozenset 类型在 Python 2.4 中引入时,它们是建立在 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 中的标准库收到了许多增强和错误修复。以下是按模块名称字母顺序排列的部分最显著变化的列表。查阅源树中的 Misc/NEWS 文件以获取更完整的变化列表,或通过 SVN 日志查看所有细节。

  • audioop 模块现在支持 a-LAW 编码,并且 u-LAW 编码的代码已得到改进。(由 Lars Immisch 贡献。)

  • codecs 模块增加了对增量编解码器的支持。codec.lookup() 函数现在返回一个 CodecInfo 实例,而不是一个元组。CodecInfo 实例表现得像一个4元组以保持向后兼容性,但也具有属性 encodedecodeincrementalencoderincrementaldecoderstreamwriterstreamreader。增量编解码器可以分多个块接收输入并生成输出;输出与将整个输入提供给非增量编解码器的输出相同。有关详细信息,请参阅 codecs 模块文档。(由 Walter Dörwald 设计并实现。)

  • collections 模块新增了一个类型 defaultdict,它是标准 dict 类型的子类。这个新类型在大多数情况下表现得像字典,但当键不存在时,会构造一个默认值,并自动将其添加到字典中,对应请求的键值。

    defaultdict 构造函数的第一个参数是一个工厂函数,当请求的键未找到时,会调用这个工厂函数。这个工厂函数不接受任何参数,因此你可以使用内置类型构造器,如 list()int()。例如,你可以基于单词的首字母创建一个索引,如下所示:

    words = """Nel mezzo del cammin di nostra vita
    mi ritrovai per una selva oscura
    che la diritta via era smarrita""".lower().split()
    
    index = defaultdict(list)
    
    for w in words:
        init_letter = w[0]
        index[init_letter].append(w)
    

    打印 index 导致以下输出:

    defaultdict(<type 'list'>, {'c': ['cammin', 'che'], 'e': ['era'],
            'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'],
            'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'],
            'p': ['per'], 's': ['selva', 'smarrita'],
            'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']})
    

    (由 Guido van Rossum 贡献。)

  • 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 参数,以便以二进制或 universal newlines 模式打开文件。 另一个新参数 openhook 允许使用除 open() 之外的其他函数来打开输入文件。 在迭代文件集合时,FileInput 对象的新 fileno() 方法返回当前打开文件的文件描述符。 (由 Georg Brandl 贡献。)

  • gc 模块中,新的 get_count() 函数返回一个包含三个 GC 世代当前收集计数的 3 元组。这是垃圾收集器的统计信息;当这些计数达到指定阈值时,将进行垃圾收集扫描。现有的 gc.collect() 函数现在接受一个可选的 generation 参数,其值为 0、1 或 2,以指定要收集的世代。(由 Barry Warsaw 贡献。)

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

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

    (由 Raymond Hettinger 贡献。)

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

    s = slice(5)     # 创建切片对象
    itertools.islice(iterable, s.start, s.stop, s.step)
    

    (由 Raymond Hettinger 贡献。)

  • locale 模块中的 format() 函数已被修改,并添加了两个新函数 format_string()currency()

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

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

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

    (由Georg Brandl 贡献。)

  • mailbox 模块进行了大规模重写,以增加修改邮箱的能力,除了读取邮箱。一组新的类,包括 mboxMHMaildir,用于读取邮箱,并具有 add(message) 方法来添加消息,remove(key) 来删除消息,以及 lock()/unlock() 来锁定/解锁邮箱。以下示例将 maildir 格式的邮箱转换为 mbox 格式的一个:

    import mailbox
    
    # 'factory=None' 使用 email.Message.Message 作为表示
    # 单个消息的类。
    src = mailbox.Maildir('maildir', factory=None)
    dest = mailbox.mbox('/tmp/mbox')
    
    for msg in src:
        dest.add(msg)
    

    (由 Gregory K. Johnson 贡献。资金由 Google 的 2005 年夏季代码计划提供。)

  • 新模块:msilib 模块允许创建 Microsoft Installer .msi 文件和 CAB 文件。还包括一些对读取 .msi 数据库的支持。(由 Martin von Löwis 贡献。)

  • nis 模块现在支持通过向 nis.match()nis.maps() 函数提供 domain 参数来访问除系统默认域之外的其他域。(由 Ben Bell 贡献。)

  • operator 模块的 itemgetter()attrgetter() 函数现在支持多个字段。例如,调用 operator.attrgetter('a', 'b') 将返回一个函数,该函数检索 ab 属性。结合这一新特性与 sort() 方法的 key 参数,可以轻松地使用多个字段对列表进行排序。(由 Raymond Hettinger 贡献。)

  • optparse 模块已更新至 Optik 库的 1.5.1 版本。OptionParser 类新增了 epilog 属性,该字符串将在帮助信息后打印,并添加了 destroy() 方法以打破对象创建的引用循环。(由 Greg Ward 贡献。)

  • os 模块进行了多项更改。stat_float_times 变量现在默认为 true,意味着 os.stat() 现在将返回浮点数的时间值。(这并不一定意味着 os.stat() 返回的时间精确到秒的小数部分;并非所有系统都支持这种精度。)

    已添加名为 os.SEEK_SETos.SEEK_CURos.SEEK_END 的常量;这些是 os.lseek() 函数的参数。用于锁定的两个新常量是 os.O_SHLOCKos.O_EXLOCK

    新增了两个函数 wait3()wait4()。它们类似于 waitpid() 函数,该函数等待子进程退出并返回进程 ID 和其退出状态的元组,但 wait3()wait4() 返回额外的信息。wait3() 不接受进程 ID 作为输入,因此它等待任何子进程退出并返回一个 3 元组,包括 process-idexit-statusresource-usage,这些信息由 resource.getrusage() 函数返回。wait4(pid) 接受进程 ID。(由 Chad J. Schroeder 贡献。)

    在FreeBSD上,os.stat() 函数现在返回具有纳秒分辨率的时间,并且返回的对象现在具有 st_genst_birthtime。如果平台支持,st_flags 属性也可用。(由 Antti Louko 和 Diego Pettenò 贡献。)

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

  • picklecPickle 模块不再接受 __reduce__() 方法返回的 None 值;该方法必须返回一个参数元组。在 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 以来已被弃用的旧 regexregsub 模块终于被删除。其他被删除的模块包括:statcachetzparsewhrandom

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

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

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

  • 由于 Philippe Biondi 提供的补丁,socket 模块现在在 Linux 上支持 AF_NETLINK 套接字。Netlink 套接字是 Linux 特有的用户空间进程与内核代码之间的通信机制;关于它们的入门文章位于 https://www.linuxjournal.com/article/7356。在 Python 代码中,netlink 地址表示为两个整数的元组 (pid, group_mask)

    套接字对象上的两个新方法 recv_into(buffer)recvfrom_into(buffer) 将接收到的数据存储在支持缓冲区协议的对象中,而不是将数据作为字符串返回。这意味着你可以直接将数据放入数组或内存映射文件中。

    套接字对象还增加了 getfamily()gettype()getproto() 访问器方法,用于检索套接字的族、类型和协议值。

  • 新模块:spwd 模块提供了在支持阴影密码的系统上访问阴影密码数据库的函数。

  • struct 模块现在更快了,因为它将格式字符串编译成带有 pack()unpack() 方法的 Struct 对象。这类似于 re 模块允许你创建编译后的正则表达式对象。你仍然可以使用模块级的 pack()unpack() 函数;它们会创建并缓存 Struct 对象。或者你可以直接使用 Struct 实例:

    s = struct.Struct('ih3s')
    
    data = s.pack(1972, 187, 'abc')
    year, number, name = s.unpack(data)
    

    你还可以使用 pack_into(buffer, offset, v1, v2, ...)unpack_from(buffer, offset) 方法直接将数据打包和解包到缓冲区对象。这允许你直接将数据存储到数组或内存映射文件中。

    (Struct 对象由 Bob Ippolito 在 NeedForSpeed sprint 中实现。缓冲区对象的支持由 Martin Blais 在同一 sprint 中添加)

  • Python 开发者在 2.5 开发过程中从 CVS 切换到了 Subversion。关于确切构建版本的信息可通过 sys.subversion 变量获取,这是一个包含 (解释器名称, 分支名称, 修订范围) 的 3 元组。例如,在撰写本文时,我的 2.5 版本报告为 ('CPython', 'trunk', '45313:45315')

    此信息也可通过 Py_GetBuildInfo() 函数供 C 扩展使用,该函数返回一个构建信息字符串,例如:"trunk:45355:45356M, Apr 13 2006, 07:42:19"。 (由 Barry Warsaw 贡献)

  • 另一个新函数 sys._current_frames() 返回所有运行线程的当前堆栈帧,作为一个字典,将线程标识符映射到在函数调用时该线程中当前活动的最顶层堆栈帧。 (由 Tim Peters 贡献)

  • tarfile 模块中的 TarFile 类现在有一个 extractall() 方法,用于将存档中的所有成员提取到当前工作目录。还可以设置不同的目录作为提取目标,并且可以仅解包存档的一部分成员。

    以流模式打开的 tarfile 的压缩方式现在可以通过模式 'r|*' 自动检测。 (由 Lars Gustäbel 贡献)

  • threading 模块现在允许你设置创建新线程时使用的堆栈大小。stack_size([*size*]) 函数返回当前配置的堆栈大小,提供可选的 size 参数设置新值。并非所有平台都支持更改堆栈大小,但 Windows、POSIX 线程和 OS/2 都支持。 (由 Andrew MacIntyre 贡献)

  • unicodedata 模块已更新为使用 Unicode 字符数据库的 4.1.0 版本。某些规范需要 3.2.0 版本,因此它仍然作为 unicodedata.ucd_3_2_0 可用。

  • 新模块:uuid 模块根据 RFC 4122 生成通用唯一标识符(UUID)。该RFC定义了从起始字符串、系统属性或纯随机生成的几种不同版本的UUID。此模块包含一个 UUID 类和名为 uuid1()uuid3()uuid4()uuid5() 的函数,用于生成不同版本的UUID。(版本2的UUID在 RFC 4122 中未指定,此模块也不支持。):

    >>> import uuid
    >>> # 基于主机ID和当前时间生成一个UUID
    >>> uuid.uuid1()
    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
    
    >>> # 使用命名空间UUID和名称的MD5哈希生成一个UUID
    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
    
    >>> # 生成一个随机UUID
    >>> uuid.uuid4()
    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
    
    >>> # 使用命名空间UUID和名称的SHA-1哈希生成一个UUID
    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
    

    (由 Ka-Ping Yee 贡献。)

  • weakref 模块的 WeakKeyDictionaryWeakValueDictionary 类型新增了用于迭代字典中包含的弱引用的方法。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 模块的 CompressDecompress 对象现在支持 copy() 方法,该方法复制对象的内部状态并返回一个新的 CompressDecompress 对象。(由 Chris AtLee 贡献。)

ctypes 包

由 Thomas Heller 编写的 ctypes 包已被添加到标准库中。ctypes 允许你调用共享库或 DLL 中的任意函数。长期用户可能还记得 dl 模块,它提供了加载共享库和调用其中函数的功能。ctypes 包则更为高级。

要加载共享库或 DLL,你必须创建一个 CDLL 类的实例,并提供共享库或 DLL 的名称或路径。 一旦完成这一步,你可以通过将其作为 CDLL 对象的属性来调用任意函数。

import ctypes

libc = ctypes.CDLL('libc.so.6')
result = libc.printf("Line of output\n")

为各种 C 类型提供了类型构造器: c_int()c_float()c_double()c_char_p() (等同于 char*),等等。 与 Python 的类型不同,C 版本的类型都是可变的;你可以通过赋值给它们的 value 属性来改变包装的值。 Python 整数和字符串将自动转换为相应的 C 类型,但对于其他类型,你必须调用正确的类型构造器。 (我强调的是 必须;弄错了通常会导致解释器崩溃,出现段错误。)

当 C 函数将修改内存区域时,你不应使用 c_char_p() 与 Python 字符串,因为 Python 字符串应该是不可变的;违反这一规则将导致令人困惑的 bug。 当你需要一个可修改的内存区域时,请使用 create_string_buffer():

s = "this is a string"
buf = ctypes.create_string_buffer(s)
libc.strfry(buf)

C 函数默认返回整数,但你可以通过设置函数对象的 restype 属性来改变这一点:

>>> libc.atof('2.71828')
-1783957616
>>> libc.atof.restype = ctypes.c_double
>>> libc.atof('2.71828')
2.71828

ctypes 还提供了一个 Python C API 的包装器,即 ctypes.pythonapi 对象。 这个对象在调用函数之前 释放全局解释器锁,因为调用解释器代码时必须持有该锁。 有一个 py_object 类型构造器,它将创建一个 PyObject* 指针。 一个简单的用法:

import ctypes

d = {}
ctypes.pythonapi.PyObject_SetItem(ctypes.py_object(d),
          ctypes.py_object("abc"),  ctypes.py_object(1))
# d 现在是 {'abc', 1}。

不要忘记使用 py_object();如果省略它,你最终会遇到段错误。

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

参见

https://web.archive.org/web/20180410025338/http://starship.python.net/crew/theller/ctypes/

预标准库 ctypes 的网页,包含教程、参考和常见问题解答。

ctypes 模块的文档。

ElementTree 包

Fredrik Lundh 的 ElementTree 库的一个子集,用于处理 XML,已被添加到标准库中作为 xml.etree。可用的模块包括从 ElementTree 1.2.6 中的 ElementTreeElementPathElementIncludecElementTree 加速模块也包括在内。

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

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

最常用的解析函数是 parse(),它接受一个字符串(假设包含文件名)或文件类对象,并返回一个 ElementTree 实例:

from xml.etree import ElementTree as ET

tree = ET.parse('ex-1.xml')

feed = urllib.urlopen(
          'http://planet.python.org/rss10.xml')
tree = ET.parse(feed)

一旦你有一个 ElementTree 实例,你可以调用它的 getroot() 方法来获取根 Element 节点。

还有一个 XML() 函数,它接受一个字符串字面量并返回一个 Element 节点(而不是 ElementTree)。这个函数提供了一种整洁的方式来整合 XML 片段,接近于 XML 字面量的便利性:

svg = ET.XML("""<svg width="10px" version="1.0">
             </svg>""")
svg.set('height', '320px')
svg.append(elem1)

每个 XML 元素都支持一些类似字典和类似列表的访问方法。类似字典的操作用于访问属性值,而类似列表的操作用于访问子节点。

运算

结果:

elem[n]

返回第n个子元素。

elem[m:n]

返回第m至第n个子元素的列表。

len(elem)

返回子元素的个数。

list(elem)

返回子元素的列表。

elem.append(elem2)

elem2 添加为子级。

elem.insert(index, elem2)

在指定位置插入 elem2

del elem[n]

删除第n个子元素。

elem.keys()

返回属性名称的列表。

elem.get(name)

返回 name 属性的值。

elem.set(name, value)

name 属性设置新值。

elem.attrib

检索包含属性的字典。

del elem.attrib[name]

删除 name 属性。

注释和处理指令也被表示为 Element 节点。要检查一个节点是否是注释或处理指令:

if elem.tag is ET.Comment:
    ...
elif elem.tag is ET.ProcessingInstruction:
    ...

要生成 XML 输出,你应该调用 ElementTree.write() 方法。与 parse() 类似,它可以接受一个字符串或类文件对象:

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

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

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

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

hashlib 包

由 Gregory P. Smith 编写的一个新的 hashlib 模块已被添加,以取代 md5sha 模块。hashlib 添加了对额外安全哈希(SHA-224、SHA-256、SHA-384 和 SHA-512)的支持。当可用时,该模块使用 OpenSSL 以实现算法的快速平台优化实现。

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

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

# 新版本
h = hashlib.md5()

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

# 新版本
h = hashlib.sha1()

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

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

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

参见

hashlib 模块的文档。

sqlite3 包

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

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

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

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

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

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

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

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

c = conn.cursor()

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

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

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

Instead, use the DB-API's parameter substitution. Put ? as a placeholder wherever you want to use a value, and then provide a tuple of values as the second argument to the cursor's execute() method. (Other database modules may use a different placeholder, such as %s or :1.) For example:

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

To retrieve data after executing a SELECT statement, you can either treat the cursor as an iterator, call the cursor's fetchone() method to retrieve a single matching row, or call fetchall() to get a list of the matching rows.

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

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

The Web Server Gateway Interface (WSGI) v1.0 defines a standard interface between web servers and Python web applications and is described in PEP 333. The wsgiref package is a reference implementation of the WSGI specification.

该软件包包含一个基本 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 的改变包括:

  • The Python source tree was converted from CVS to Subversion, in a complex migration procedure that was supervised and flawlessly carried out by Martin von Löwis. The procedure was developed as PEP 347.

  • Coverity 公司,一家销售名为 Prevent 的源代码分析工具的公司,提供了他们对 Python 源代码的检查结果。分析发现了大约 60 个 bug,这些 bug 很快就被修复了。许多 bug 是引用计数问题,通常出现在错误处理代码中。请参阅 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 指定符,且周围没有文本。

  • 库:picklecPickle 模块不再接受 __reduce__() 方法返回值为 None;该方法必须返回一个参数元组。这些模块也不再接受已弃用的 bin 关键字参数。

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

  • C API:许多函数现在使用 Py_ssize_t 而不是 int,以允许在 64 位机器上处理更多数据。扩展代码可能需要做同样的更改,以避免警告并支持 64 位机器。请参阅之前的章节 PEP 353: 使用ssize_t作为索引类型 了解有关此更改的讨论。

  • C API:obmalloc 的更改意味着你必须小心,不要混合使用 PyMem_*PyObject_* 系列函数。使用一个系列的 *_Malloc 分配的内存必须使用相应系列的 *_Free 函数释放。

致谢

作者感谢以下人员对本文各种草稿给予的建议,更正和协助: Georg Brandl, Nick Coghlan, Phillip J. Eby, Lars Gustäbel, Raymond Hettinger, Ralf W. Grosse-Kunstleve, Kent Johnson, Iain Lowe, Martin von Löwis, Fredrik Lundh, Andrew McNamara, Skip Montanaro, Gustavo Niemeyer, Paul Prescod, James Pryor, Mike Rovner, Scott Weikart, Barry Warsaw, Thomas Wouters.