Python 2.3 新特性

作者:

A.M. Kuchling

本文介绍了 Python 2.3 的新功能。Python 2.3 于 2003 年 7 月 29 日发布。

Python 2.3 的主要主题是完善 2.2 中添加的一些功能,为核心语言添加各种小的但有用的增强功能,以及扩展标准库。上一版本中引入的新对象模型已从 18 个月的错误修复和优化工作中受益,这些工作提高了新式类的性能。添加了一些新的内置函数,例如 sum()enumerate()in 运算符现在可以用于子字符串搜索(例如,"ab" in "abc" 返回 True)。

许多新库功能包括布尔型、集合、堆和日期/时间数据类型,从 ZIP 格式的存档导入模块的功能,对期待已久的 Python 目录的元数据支持,IDLE 的更新版本,以及用于记录消息、包装文本、解析 CSV 文件、处理命令行选项、使用 BerkeleyDB 数据库的模块…… 新的和增强的模块列表很长。

本文不试图提供新功能的完整规范,而是提供一个方便的概述。有关完整详细信息,您应该参考 Python 2.3 的文档,例如 Python 库参考和 Python 参考手册。如果您想了解完整的实现和设计原理,请参考特定新功能的 PEP。

PEP 218:标准集合数据类型

新的 sets 模块包含集合数据类型的实现。Set 类用于可变集合,即可添加和删除成员的集合。ImmutableSet 类用于不可修改的集合,因此 ImmutableSet 的实例可以用作字典键。集合构建在字典之上,因此集合中的元素必须是可哈希的。

这是一个简单的示例

>>> import sets
>>> S = sets.Set([1,2,3])
>>> S
Set([1, 2, 3])
>>> 1 in S
True
>>> 0 in S
False
>>> S.add(5)
>>> S.remove(3)
>>> S
Set([1, 2, 5])
>>>

集合的并集和交集可以使用 union()intersection() 方法计算;另一种表示法是使用按位运算符 &|。可变集合也有这些方法的就地版本,union_update()intersection_update()

>>> S1 = sets.Set([1,2,3])
>>> S2 = sets.Set([4,5,6])
>>> S1.union(S2)
Set([1, 2, 3, 4, 5, 6])
>>> S1 | S2                  # Alternative notation
Set([1, 2, 3, 4, 5, 6])
>>> S1.intersection(S2)
Set([])
>>> S1 & S2                  # Alternative notation
Set([])
>>> S1.union_update(S2)
>>> S1
Set([1, 2, 3, 4, 5, 6])
>>>

也可以取两个集合的对称差。这是并集中所有不在交集中的元素的集合。另一种说法是,对称差包含恰好在一个集合中的所有元素。同样,有一种替代表示法 (^),以及一个名称笨拙的就地版本 symmetric_difference_update()

>>> S1 = sets.Set([1,2,3,4])
>>> S2 = sets.Set([3,4,5,6])
>>> S1.symmetric_difference(S2)
Set([1, 2, 5, 6])
>>> S1 ^ S2
Set([1, 2, 5, 6])
>>>

还有 issubset()issuperset() 方法用于检查一个集合是否是另一个集合的子集或超集

>>> S1 = sets.Set([1,2,3])
>>> S2 = sets.Set([2,3])
>>> S2.issubset(S1)
True
>>> S1.issubset(S2)
False
>>> S1.issuperset(S2)
True
>>>

另请参阅

PEP 218 - 添加内置集合对象类型

由 Greg V. Wilson 编写的 PEP。由 Greg V. Wilson、Alex Martelli 和 GvR 实现。

PEP 255:简单生成器

在 Python 2.2 中,生成器作为可选功能添加,可以通过 from __future__ import generators 指令启用。在 2.3 中,生成器不再需要特殊启用,现在始终存在;这意味着 yield 现在始终是一个关键字。本节的其余部分是“Python 2.2 新特性”文档中生成器的描述副本;如果您在 Python 2.2 发布时读过它,则可以跳过本节的其余部分。

您无疑熟悉函数调用在 Python 或 C 中是如何工作的。当您调用一个函数时,它会获得一个私有命名空间,在其中创建其局部变量。当函数到达 return 语句时,局部变量将被销毁,并将结果值返回给调用者。稍后调用同一个函数将获得一组全新的局部变量。但是,如果局部变量在退出函数时没有被丢弃会怎么样呢?如果您稍后可以从函数离开的地方恢复它会怎么样呢?这就是生成器提供的功能;它们可以被认为是可恢复的函数。

这是生成器函数的最简单示例

def generate_ints(N):
    for i in range(N):
        yield i

为生成器引入了一个新的关键字 yield。任何包含 yield 语句的函数都是生成器函数;这是由 Python 的字节码编译器检测到的,该编译器会因此对函数进行特殊编译。

当您调用生成器函数时,它不会返回单个值;相反,它返回一个支持迭代器协议的生成器对象。在执行 yield 语句时,生成器会输出 i 的值,类似于 return 语句。yieldreturn 语句之间的最大区别在于,当到达 yield 时,生成器的执行状态会被挂起,并且局部变量会被保留。在下次调用生成器的 .next() 方法时,函数将立即从 yield 语句之后恢复执行。(由于复杂的原因,不允许在 tryfinally 语句的 try 块内部使用 yield 语句;请阅读 PEP 255 以全面解释 yield 和异常之间的相互作用。)

这是 generate_ints() 生成器的示例用法

>>> gen = generate_ints(3)
>>> gen
<generator object at 0x8117f90>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
  File "stdin", line 1, in ?
  File "stdin", line 2, in generate_ints
StopIteration

您可以同样编写 for i in generate_ints(5)a,b,c = generate_ints(3)

在生成器函数内部,return 语句只能不带值使用,并表示值序列的结束;之后生成器不能再返回任何值。带值的 return,例如 return 5,在生成器函数内部是语法错误。生成器结果的结束也可以通过手动引发 StopIteration 来指示,或者只是让执行流程自然地到达函数的底部。

你可以通过编写自己的类并将生成器的所有局部变量存储为实例变量来手动实现生成器的效果。例如,返回整数列表可以通过将 self.count 设置为 0,并让 next() 方法递增 self.count 并返回它来实现。然而,对于一个中等复杂的生成器,编写相应的类会更加麻烦。Lib/test/test_generators.py 包含一些更有趣的示例。最简单的示例是使用生成器递归实现树的顺序遍历。

# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
    if t:
        for x in inorder(t.left):
            yield x
        yield t.label
        for x in inorder(t.right):
            yield x

Lib/test/test_generators.py 中的另外两个示例为 N 皇后问题(在 NxN 棋盘上放置 N 个皇后,使任何皇后都不会威胁到另一个皇后)和骑士巡逻问题(让骑士在 NxN 棋盘上走遍每个格子且不重复访问任何格子)提供了解决方案。

生成器的概念来自其他编程语言,特别是 Icon (https://www2.cs.arizona.edu/icon/),其中生成器的概念是核心。在 Icon 中,每个表达式和函数调用都像一个生成器。“Icon 编程语言概述”(https://www2.cs.arizona.edu/icon/docs/ipd266.htm) 中的一个示例可以让你了解它的样子

sentence := "Store it in the neighboring harbor"
if (i := find("or", sentence)) > 5 then write(i)

在 Icon 中,find() 函数返回子字符串 “or” 出现的索引:3、23、33。在 if 语句中,i 首先被赋值为 3,但 3 小于 5,因此比较失败,Icon 使用第二个值 23 重试。23 大于 5,因此比较现在成功,代码将值 23 打印到屏幕。

Python 在采用生成器作为核心概念方面远不如 Icon。生成器被认为是 Python 核心语言的一部分,但学习或使用它们不是强制性的;如果它们没有解决你遇到的任何问题,请随意忽略它们。与 Icon 相比,Python 接口的一个新颖之处在于,生成器的状态被表示为一个具体的对象(迭代器),它可以传递给其他函数或存储在数据结构中。

另请参阅

PEP 255 - 简单生成器

由 Neil Schemenauer、Tim Peters、Magnus Lie Hetland 编写。主要由 Neil Schemenauer 和 Tim Peters 实现,Python Labs 团队进行了其他修复。

PEP 263: 源代码编码

现在可以将 Python 源代码文件声明为使用不同的字符集编码。通过在源文件的第一行或第二行中包含一个特殊格式的注释来声明编码。例如,可以使用以下方式声明 UTF-8 文件

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

如果没有这样的编码声明,则使用的默认编码是 7 位 ASCII。执行或导入包含带 8 位字符的字符串字面量且没有编码声明的模块会导致 Python 2.3 发出 DeprecationWarning;在 2.4 中,这将是一个语法错误。

编码声明仅影响 Unicode 字符串字面量,这些字面量将使用指定的编码转换为 Unicode。请注意,Python 标识符仍然限制为 ASCII 字符,因此你不能使用超出常用字母数字的字符作为变量名。

另请参阅

PEP 263 - 定义 Python 源代码编码

由 Marc-André Lemburg 和 Martin von Löwis 编写;由 Suzuki Hisao 和 Martin von Löwis 实现。

PEP 273: 从 ZIP 存档导入模块

新的 zipimport 模块增加了对从 ZIP 格式存档导入模块的支持。你不需要显式导入该模块;如果 ZIP 存档的文件名添加到 sys.path,它将自动导入。例如

amk@nyman:~/src/python$ unzip -l /tmp/example.zip
Archive:  /tmp/example.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
     8467  11-26-02 22:30   jwzthreading.py
 --------                   -------
     8467                   1 file
amk@nyman:~/src/python$ ./python
Python 2.3 (#1, Aug 1 2003, 19:54:32)
>>> import sys
>>> sys.path.insert(0, '/tmp/example.zip')  # Add .zip file to front of path
>>> import jwzthreading
>>> jwzthreading.__file__
'/tmp/example.zip/jwzthreading.py'
>>>

sys.path 中的条目现在可以是 ZIP 存档的文件名。ZIP 存档可以包含任何类型的文件,但只有名为 *.py*.pyc*.pyo 的文件可以导入。如果存档仅包含 *.py 文件,Python 将不会尝试通过添加相应的 *.pyc 文件来修改存档,这意味着如果 ZIP 存档不包含 *.pyc 文件,则导入可能会相当慢。

还可以指定存档中的路径,以便仅从子目录导入;例如,路径 /tmp/example.zip/lib/ 将仅从存档中的 lib/ 子目录导入。

另请参阅

PEP 273 - 从 Zip 存档导入模块

由 James C. Ahlstrom 编写,他同时也提供了实现。Python 2.3 遵循 PEP 273 中的规范,但使用 Just van Rossum 编写的实现,该实现使用 PEP 302 中描述的导入钩子。有关新导入钩子的描述,请参阅 PEP 302:新的导入钩子 部分。

PEP 277: Windows NT 的 Unicode 文件名支持

在 Windows NT、2000 和 XP 上,系统将文件名存储为 Unicode 字符串。传统上,Python 将文件名表示为字节字符串,这不足以满足需求,因为它会导致某些文件名无法访问。

现在 Python 允许对所有需要文件名的函数使用任意 Unicode 字符串(在文件系统的限制范围内),最值得注意的是 open() 内置函数。如果将 Unicode 字符串传递给 os.listdir(),Python 现在会返回一个 Unicode 字符串列表。一个新的函数 os.getcwdu() 返回当前目录的 Unicode 字符串。

字节字符串仍然可以用作文件名,在 Windows 上,Python 会使用 mbcs 编码将它们透明地转换为 Unicode。

其他系统也允许使用 Unicode 字符串作为文件名,但在将它们传递给系统之前会将它们转换为字节字符串,这可能会导致引发 UnicodeError。应用程序可以通过检查 os.path.supports_unicode_filenames(一个布尔值)来测试是否支持任意 Unicode 字符串作为文件名。

在 MacOS 下,os.listdir() 现在可能会返回 Unicode 文件名。

另请参阅

PEP 277 - Windows NT 的 Unicode 文件名支持

由 Neil Hodgson 编写;由 Neil Hodgson、Martin von Löwis 和 Mark Hammond 实现。

PEP 278: 通用换行符支持

当今使用的三大操作系统是 Microsoft Windows、Apple 的 Macintosh OS 和各种 Unix 衍生系统。跨平台工作的一个小麻烦是,这三个平台都使用不同的字符来标记文本文件中行的结尾。Unix 使用换行符(ASCII 字符 10),MacOS 使用回车符(ASCII 字符 13),Windows 使用回车符加换行符的两个字符序列。

现在,Python 的文件对象可以支持除 Python 运行平台所遵循的换行符约定之外的其他约定。使用模式 'U''rU' 打开文件将以通用换行符模式打开文件进行读取。所有三种换行符约定都将被转换为 '\n',并返回到各种文件方法(例如 read()readline())返回的字符串中。

通用换行符支持也用于导入模块和使用 execfile() 函数执行文件时。这意味着 Python 模块可以在所有三个操作系统之间共享,而无需转换换行符。

在编译 Python 时,可以通过在运行 Python 的 configure 脚本时指定 --without-universal-newlines 开关来禁用此功能。

另请参阅

PEP 278 - 通用换行符支持

由 Jack Jansen 编写和实现。

PEP 279: enumerate()

一个新的内置函数 enumerate() 将使某些循环更清晰。enumerate(thing),其中 *thing* 是一个迭代器或一个序列,它返回一个迭代器,该迭代器将返回 (0, thing[0])(1, thing[1])(2, thing[2]) 等等。

一个用于更改列表的每个元素的常见用法如下所示

for i in range(len(L)):
    item = L[i]
    # ... compute some result based on item ...
    L[i] = result

可以使用 enumerate() 将其重写为

for i, item in enumerate(L):
    # ... compute some result based on item ...
    L[i] = result

另请参阅

PEP 279 - enumerate() 内置函数

由 Raymond D. Hettinger 编写和实现。

PEP 282: logging 包

一个用于编写日志的标准包,logging,已添加到 Python 2.3 中。它提供了一个强大而灵活的机制来生成日志输出,然后可以通过各种方式对其进行过滤和处理。可以使用以标准格式编写的配置文件来控制程序的日志记录行为。Python 包括将日志记录写入标准错误或文件或套接字的处理器,将它们发送到系统日志,甚至通过电子邮件将它们发送到特定地址;当然,也可以编写自己的处理器类。

Logger 类是主要类。大多数应用程序代码将处理一个或多个 Logger 对象,每个对象由应用程序的特定子系统使用。每个 Logger 都由一个名称标识,并且名称使用 . 作为组件分隔符组织成一个层次结构。例如,您可能有名为 serverserver.authserver.networkLogger 实例。后两个实例在层次结构中位于 server 的下方。这意味着如果您提高 server 的详细程度或将 server 消息定向到不同的处理器,则更改也将应用于记录到 server.authserver.network 的记录。还有一个根 Logger,它是所有其他记录器的父级。

对于简单的用法,logging 包包含一些始终使用根日志的便捷函数

import logging

logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')

这将产生以下输出

WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down

在默认配置中,信息和调试消息将被抑制,并且输出将发送到标准错误。您可以通过调用根记录器的 setLevel() 方法来启用信息和调试消息的显示。

请注意 warning() 调用如何使用字符串格式化运算符;所有用于记录消息的函数都采用参数 (msg, arg1, arg2, ...) 并记录来自 msg % (arg1, arg2, ...) 的字符串。

还有一个 exception() 函数,用于记录最近的回溯。如果您为关键字参数 *exc_info* 指定一个真值,任何其他函数也会记录回溯。

def f():
    try:    1/0
    except: logging.exception('Problem recorded')

f()

这将产生以下输出

ERROR:root:Problem recorded
Traceback (most recent call last):
  File "t.py", line 6, in f
    1/0
ZeroDivisionError: integer division or modulo by zero

稍微高级的程序将使用根记录器之外的记录器。 getLogger(name) 函数用于获取特定的日志,如果该日志尚不存在,则会创建它。getLogger(None) 返回根记录器。

log = logging.getLogger('server')
 ...
log.info('Listening on port %i', port)
 ...
log.critical('Disk full')
 ...

日志记录通常会在层次结构中向上传播,因此记录到 server.auth 的消息也会被 serverroot 看到,但是 Logger 可以通过将其 propagate 属性设置为 False 来阻止这种情况。

logging 包提供了更多可以自定义的类。当告知 Logger 实例记录消息时,它会创建一个 LogRecord 实例,该实例被发送到任意数量的不同 Handler 实例。记录器和处理器还可以附加过滤器列表,每个过滤器都可以导致 LogRecord 被忽略,或者可以在传递记录之前对其进行修改。当它们最终输出时,LogRecord 实例由 Formatter 类转换为文本。所有这些类都可以替换为您自己专门编写的类。

凭借所有这些功能,logging 包应该为即使是最复杂的应用程序也提供足够的灵活性。这只是其功能的不完整概述,因此请参阅该包的参考文档以获取所有详细信息。阅读PEP 282 也将有所帮助。

另请参阅

PEP 282 - 日志系统

由 Vinay Sajip 和 Trent Mick 编写;由 Vinay Sajip 实现。

PEP 285:布尔类型

布尔类型已添加到 Python 2.3 中。两个新的常量被添加到 __builtin__ 模块中,即 TrueFalse。(TrueFalse 常量已在 Python 2.2.1 中添加到内置函数中,但 2.2.1 版本只是简单地设置为整数值 1 和 0,而不是不同的类型。)

此新类型的类型对象名为 bool;它的构造函数接受任何 Python 值并将其转换为 TrueFalse

>>> bool(1)
True
>>> bool(0)
False
>>> bool([])
False
>>> bool( (1,) )
True

大多数标准库模块和内置函数都已更改为返回布尔值。

>>> obj = []
>>> hasattr(obj, 'append')
True
>>> isinstance(obj, list)
True
>>> isinstance(obj, tuple)
False

添加 Python 布尔值的主要目标是使代码更清晰。例如,如果你正在阅读一个函数并遇到语句 return 1,你可能会想知道 1 代表布尔真值、索引还是乘以某个其他数量的系数。但是,如果语句是 return True,则返回值的含义非常清楚。

添加 Python 布尔值不是为了严格的类型检查。像 Pascal 这样非常严格的语言也会阻止你对布尔值执行算术运算,并且会要求 if 语句中的表达式始终计算为布尔结果。Python 不是那么严格,也永远不会那么严格,正如PEP 285 明确指出的那样。这意味着你仍然可以在 if 语句中使用任何表达式,即使是那些计算结果为列表、元组或某些随机对象的表达式。布尔类型是 int 类的子类,因此使用布尔值的算术运算仍然有效。

>>> True + 1
2
>>> False + 1
1
>>> False * 75
0
>>> True * 75
75

总而言之,用一句话概括 TrueFalse:它们是拼写整数值 1 和 0 的另一种方式,唯一的区别在于 str()repr() 返回字符串 'True''False' 而不是 '1''0'

另请参阅

PEP 285 - 添加布尔类型

由 GvR 编写和实现。

PEP 293:编解码器错误处理回调

将 Unicode 字符串编码为字节字符串时,可能会遇到无法编码的字符。到目前为止,Python 允许将错误处理指定为“strict”(引发 UnicodeError)、“ignore”(跳过字符)或“replace”(在输出字符串中使用问号),其中“strict”是默认行为。可能需要指定此类错误的其他处理方式,例如在转换后的字符串中插入 XML 字符引用或 HTML 实体引用。

Python 现在有一个灵活的框架来添加不同的处理策略。可以使用 codecs.register_error() 添加新的错误处理程序,然后编解码器可以使用 codecs.lookup_error() 访问错误处理程序。对于用 C 编写的编解码器,已添加等效的 C API。错误处理程序获取必要的状态信息,例如正在转换的字符串、检测到错误时字符串中的位置以及目标编码。然后,处理程序可以引发异常或返回替换字符串。

使用此框架实现了两个额外的错误处理程序:“backslashreplace” 使用 Python 反斜杠引用来表示无法编码的字符,“xmlcharrefreplace” 发出 XML 字符引用。

另请参阅

PEP 293 - 编解码器错误处理回调

由 Walter Dörwald 编写和实现。

PEP 301:Distutils 的包索引和元数据

对长期请求的 Python 目录的支持在 2.3 中首次出现。

目录的核心是新的 Distutils register 命令。运行 python setup.py register 将收集描述包的元数据,例如其名称、版本、维护者、描述等,并将其发送到中央目录服务器。结果目录可从 https://pypi.ac.cn 获得。

为了使目录更有用,新的可选 classifiers 关键字参数已添加到 Distutils setup() 函数中。可以提供一个 Trove 样式字符串列表来帮助对软件进行分类。

这是一个带有分类器的 setup.py 示例,旨在与旧版本的 Distutils 兼容

from distutils import core
kw = {'name': "Quixote",
      'version': "0.5.1",
      'description': "A highly Pythonic Web application framework",
      # ...
      }

if (hasattr(core, 'setup_keywords') and
    'classifiers' in core.setup_keywords):
    kw['classifiers'] = \
        ['Topic :: Internet :: WWW/HTTP :: Dynamic Content',
         'Environment :: No Input/Output (Daemon)',
         'Intended Audience :: Developers'],

core.setup(**kw)

可以通过运行 python setup.py register --list-classifiers 获取分类器的完整列表。

另请参阅

PEP 301 - Distutils 的包索引和元数据

由 Richard Jones 编写和实现。

PEP 302:新的导入钩子

虽然自从 Python 1.3 中引入 ihooks 模块以来,编写自定义导入钩子一直都是可能的,但没有人真正对此感到满意,因为编写新的导入钩子既困难又麻烦。已经有各种提议的替代方案,例如 imputiliu 模块,但它们都没有获得太多的认可,并且都没有容易地从 C 代码中使用。

PEP 302 从其前辈那里借鉴了想法,特别是从 Gordon McMillan 的 iu 模块中。三个新项已添加到 sys 模块中

  • sys.path_hooks 是可调用对象的列表;它们通常是类。每个可调用对象都接受一个包含路径的字符串,并返回一个将处理此路径导入的导入器对象,如果它无法处理此路径,则引发 ImportError 异常。

  • sys.path_importer_cache 缓存每个路径的导入器对象,因此每个路径只需要遍历一次 sys.path_hooks

  • sys.meta_path 是将在检查 sys.path 之前遍历的导入器对象列表。此列表最初为空,但用户代码可以向其中添加对象。可以通过添加到此列表的对象导入其他内置和冻结模块。

导入器对象必须有一个方法 find_module(fullname, path=None)fullname 将是一个模块或包名称,例如 stringdistutils.corefind_module() 必须返回一个加载器对象,该对象具有一个方法 load_module(fullname),该方法创建并返回相应的模块对象。

因此,Python 新导入逻辑的伪代码看起来像这样(稍微简化了一些;有关完整详细信息,请参阅PEP 302

for mp in sys.meta_path:
    loader = mp(fullname)
    if loader is not None:
        <module> = loader.load_module(fullname)

for path in sys.path:
    for hook in sys.path_hooks:
        try:
            importer = hook(path)
        except ImportError:
            # ImportError, so try the other path hooks
            pass
        else:
            loader = importer.find_module(fullname)
            <module> = loader.load_module(fullname)

# Not found!
raise ImportError

另请参阅

PEP 302 - 新的导入钩子

由 Just van Rossum 和 Paul Moore 编写。由 Just van Rossum 实现。

PEP 305:逗号分隔文件

逗号分隔文件是一种常用于从数据库和电子表格导出数据的格式。Python 2.3 添加了一个逗号分隔文件的解析器。

乍一看,逗号分隔格式非常简单

Costs,150,200,3.95

读取一行并调用 line.split(','):还有什么更简单的?但是,如果包含可以包含逗号的字符串数据,事情就会变得更加复杂

"Costs",150,200,3.95,"Includes taxes, shipping, and sundry items"

一个巨大的丑陋正则表达式可以解析它,但是使用新的 csv 包要简单得多

import csv

input = open('datafile', 'rb')
reader = csv.reader(input)
for line in reader:
    print line

reader() 函数接受许多不同的选项。字段分隔符不限于逗号,可以更改为任何字符,引号和行尾字符也可以更改。

可以定义和注册不同的逗号分隔文件方言;目前有两种方言,都由 Microsoft Excel 使用。一个单独的 csv.writer 类将从一系列元组或列表中生成逗号分隔文件,并引用包含分隔符的字符串。

另请参阅

PEP 305 - CSV 文件 API

由 Kevin Altis、Dave Cole、Andrew McNamara、Skip Montanaro、Cliff Wells 编写和实现。

PEP 307:Pickle 增强

picklecPickle 模块在 2.3 开发周期中受到了一些关注。在 2.2 中,新式类可以毫无困难地进行 pickle 操作,但它们的 pickle 操作并不十分紧凑;PEP 307 引用了一个简单的示例,其中一个新式类导致的 pickle 字符串的长度是经典类的三倍。

解决方案是发明一种新的 pickle 协议。pickle.dumps() 函数长期以来都支持文本或二进制标志。在 2.3 中,此标志从布尔值重新定义为整数:0 是旧的文本模式 pickle 格式,1 是旧的二进制格式,而现在 2 是新的 2.3 特定格式。可以使用新的常量 pickle.HIGHEST_PROTOCOL 来选择可用的最先进的协议。

现在,取消 pickle 操作不再被认为是安全操作。2.2 的 pickle 提供了钩子,用于尝试阻止取消 pickle 不安全的类(特别是 __safe_for_unpickling__ 属性),但没有任何代码经过审核,因此所有这些代码都在 2.3 中被删除。在任何 Python 版本中,都不应该取消 pickle 不受信任的数据。

为了减少新式类的 pickle 开销,添加了一个新的接口,用于使用三个特殊方法自定义 pickle 操作:__getstate__()__setstate__()__getnewargs__()。有关这些方法的完整语义,请查阅 PEP 307

作为进一步压缩 pickle 的一种方法,现在可以使用整数代码而不是长字符串来标识 pickle 的类。Python 软件基金会将维护一个标准代码列表;还有一系列供私人使用的代码。目前尚未指定任何代码。

另请参阅

PEP 307 - pickle 协议的扩展

由 Guido van Rossum 和 Tim Peters 编写和实现。

扩展切片

自从 Python 1.4 以来,切片语法就支持可选的第三个“步长”或“跨距”参数。例如,这些都是合法的 Python 语法:L[1:10:2]L[:-1:1]L[::-1]。这是应 Numerical Python 开发人员的要求添加到 Python 的,他们广泛使用第三个参数。但是,Python 的内置列表、元组和字符串序列类型从未支持此功能,如果尝试使用,则会引发 TypeError。Michael Hudson 贡献了一个补丁来修复此缺陷。

例如,现在可以轻松提取具有偶数索引的列表元素

>>> L = range(10)
>>> L[::2]
[0, 2, 4, 6, 8]

负值也可以用于以相反的顺序复制相同的列表

>>> L[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

这也适用于元组、数组和字符串

>>> s='abcd'
>>> s[::2]
'ac'
>>> s[::-1]
'dcba'

如果有一个可变序列,例如列表或数组,则可以赋值或删除扩展切片,但对扩展切片赋值和对常规切片赋值之间存在一些差异。对常规切片的赋值可以用来更改序列的长度

>>> a = range(3)
>>> a
[0, 1, 2]
>>> a[1:3] = [4, 5, 6]
>>> a
[0, 4, 5, 6]

扩展切片没有那么灵活。在赋值给扩展切片时,语句右侧的列表必须包含与它所替换的切片相同数量的项

>>> a = range(4)
>>> a
[0, 1, 2, 3]
>>> a[::2]
[0, 2]
>>> a[::2] = [0, -1]
>>> a
[0, 1, -1, 3]
>>> a[::2] = [0,1,2]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: attempt to assign sequence of size 3 to extended slice of size 2

删除更直接

>>> a = range(4)
>>> a
[0, 1, 2, 3]
>>> a[::2]
[0, 2]
>>> del a[::2]
>>> a
[1, 3]

现在还可以将切片对象传递给内置序列的 __getitem__() 方法

>>> range(10).__getitem__(slice(0, 5, 2))
[0, 2, 4]

或者直接在下标中使用切片对象

>>> range(10)[slice(0, 5, 2)]
[0, 2, 4]

为了简化实现支持扩展切片的序列,切片对象现在有一个方法 indices(length),给定一个序列的长度,该方法返回一个可以传递给 range()(start, stop, step) 元组。indices() 以与常规切片一致的方式处理省略的和超出范围的索引(这个不起眼的短语隐藏了一系列令人困惑的细节!)。该方法旨在像这样使用

class FakeSeq:
    ...
    def calc_item(self, i):
        ...
    def __getitem__(self, item):
        if isinstance(item, slice):
            indices = item.indices(len(self))
            return FakeSeq([self.calc_item(i) for i in range(*indices)])
        else:
            return self.calc_item(i)

从这个例子中,你还可以看到内置的 slice 对象现在是切片类型的类型对象,不再是函数。这与 Python 2.2 一致,其中 intstr 等经历了相同的变化。

其他语言更改

以下是 Python 2.3 对核心 Python 语言所做的所有更改。

  • 如本文档的 PEP 255:简单生成器 部分所述,yield 语句现在始终是一个关键字。

  • 如本文档的 PEP 279:enumerate() 部分所述,添加了一个新的内置函数 enumerate()

  • 如本文档的 PEP 285:布尔类型 部分所述,添加了两个新的常量 TrueFalse 以及内置的 bool 类型。

  • 当字符串或浮点数太大而无法放入整数时,int() 类型构造函数现在将返回一个长整数,而不是引发 OverflowError。这可能会导致 isinstance(int(expression), int) 为 false 的自相矛盾的结果,但这似乎不太可能在实践中引起问题。

  • 内置类型现在支持扩展切片语法,如本文档的 扩展切片 部分所述。

  • 一个新的内置函数 sum(iterable, start=0),它将可迭代对象中的数字项相加并返回它们的和。sum() 只接受数字,这意味着不能用它来连接一堆字符串。(由 Alex Martelli 贡献。)

  • pos 为负数时,list.insert(pos, value) 过去会在列表的前面插入 value。现在该行为已更改为与切片索引一致,因此当 pos 为 -1 时,该值将插入到最后一个元素之前,依此类推。

  • list.index(value) 在列表中搜索 value 并返回其索引,现在采用可选的 startstop 参数,以将搜索限制到列表的某个部分。

  • 字典有一个新方法 pop(key[, *default*]),它返回与 key 对应的值并从字典中删除该键/值对。如果所请求的键不存在于字典中,则如果指定了 default,则返回 default,如果没有指定,则会引发 KeyError

    >>> d = {1:2}
    >>> d
    {1: 2}
    >>> d.pop(4)
    Traceback (most recent call last):
      File "stdin", line 1, in ?
    KeyError: 4
    >>> d.pop(1)
    2
    >>> d.pop(1)
    Traceback (most recent call last):
      File "stdin", line 1, in ?
    KeyError: 'pop(): dictionary is empty'
    >>> d
    {}
    >>>
    

    还有一个新的类方法 dict.fromkeys(iterable, value),它创建一个字典,其键取自提供的迭代器 iterable,并且所有值都设置为 value,默认为 None

    (由 Raymond Hettinger 贡献的补丁。)

    此外,dict() 构造函数现在接受关键字参数,以简化创建小型字典的过程

    >>> dict(red=1, blue=2, green=3, black=4)
    {'blue': 2, 'black': 4, 'green': 3, 'red': 1}
    

    (由 Just van Rossum 贡献。)

  • >>> import types
    >>> m = types.ModuleType('abc','docstring')
    >>> m
    <module 'abc' (built-in)>
    >>> m.__doc__
    'docstring'
    
  • >>> s = socket.socket()
    >>> s.__class__
    <type 'socket'>
    

    >>> s.__class__
    <type '_socket.socket'>
    

  • >>> 'ab' in 'abcd'
    True
    >>> 'ad' in 'abcd'
    False
    >>> '' in 'abcd'
    True
    

  • >>> '   abc '.strip()
    'abc'
    >>> '><><abc<><><>'.strip('<>')
    'abc'
    >>> '><><abc<><><>\n'.strip('<>')
    'abc<><><>\n'
    >>> u'\u4000\u4001abc\u4000'.strip(u'\u4000')
    u'\u4001abc'
    >>>
    

  • >>> '45'.zfill(4)
    '0045'
    >>> '12345'.zfill(4)
    '12345'
    >>> 'goofy'.zfill(6)
    '0goofy'
    

  • array 模块现在支持使用 'u' 格式字符的 Unicode 字符数组。数组现在还支持使用 += 赋值运算符来添加另一个数组的内容,以及使用 *= 赋值运算符来重复一个数组。(由 Jason Orendorff 贡献)。

  • bsddb 模块已被 PyBSDDB 包的 4.1.6 版本替换,为 BerkeleyDB 库的事务功能提供了更完整的接口。

    旧版本的模块已重命名为 bsddb185,并且不再自动构建;您需要编辑 Modules/Setup 来启用它。请注意,新的 bsddb 包旨在与旧模块兼容,因此如果您发现任何不兼容之处,请务必提交错误报告。当升级到 Python 2.3 时,如果新的解释器是使用底层 BerkeleyDB 库的新版本编译的,那么您几乎肯定需要将数据库文件转换为新版本。您可以使用发行版的 Tools/scripts 目录中的新脚本 db2pickle.pypickle2db.py 轻松完成此操作。如果您之前一直在使用 PyBSDDB 包并将其作为 bsddb3 导入,则需要更改 import 语句将其作为 bsddb 导入。

  • 新的 bz2 模块是 bz2 数据压缩库的接口。bz2 压缩的数据通常比相应的 zlib 压缩的数据更小。(由 Gustavo Niemeyer 贡献)。

  • 新的 datetime 模块中添加了一组标准日期/时间类型。有关更多详细信息,请参阅以下部分。

  • Distutils Extension 类现在支持一个名为 depends 的额外构造函数参数,用于列出扩展所依赖的其他源文件。如果任何依赖文件被修改,这允许 Distutils 重新编译模块。例如,如果 sampmodule.c 包含头文件 sample.h,则可以像这样创建 Extension 对象

    ext = Extension("samp",
                    sources=["sampmodule.c"],
                    depends=["sample.h"])
    

    然后,修改 sample.h 将导致模块被重新编译。(由 Jeremy Hylton 贡献)。

  • Distutils 的其他一些小改动:它现在检查 CCCFLAGSCPPLDFLAGSCPPFLAGS 环境变量,使用它们来覆盖 Python 配置中的设置 (由 Robert Weber 贡献)。

  • 以前,doctest 模块只会搜索公共方法和函数的文档字符串以查找测试用例,但现在它也会检查私有方法和函数。DocTestSuite() 函数从一组 doctest 测试创建一个 unittest.TestSuite 对象。

  • 新的 gc.get_referents(object) 函数返回 object 引用的所有对象的列表。

  • getopt 模块获得了一个新函数 gnu_getopt(),它支持与现有 getopt() 函数相同的参数,但使用 GNU 风格的扫描模式。现有的 getopt() 在遇到非选项参数时会立即停止处理选项,但在 GNU 风格的模式下,处理会继续,这意味着选项和参数可以混合使用。例如

    >>> getopt.getopt(['-f', 'filename', 'output', '-v'], 'f:v')
    ([('-f', 'filename')], ['output', '-v'])
    >>> getopt.gnu_getopt(['-f', 'filename', 'output', '-v'], 'f:v')
    ([('-f', 'filename'), ('-v', '')], ['output'])
    

    (由 Peter Åstrand 贡献)。

  • grppwdresource 模块现在返回增强的元组

    >>> import grp
    >>> g = grp.getgrnam('amk')
    >>> g.gr_name, g.gr_gid
    ('amk', 500)
    
  • gzip 模块现在可以处理超过 2 GiB 的文件。

  • 新的 heapq 模块包含堆队列算法的实现。堆是一种类似数组的数据结构,它将项目保持在部分排序的顺序,使得对于每个索引 kheap[k] <= heap[2*k+1]heap[k] <= heap[2*k+2]。这使得删除最小的项目速度很快,并且在保持堆属性的同时插入新项目是 O(log n)。(有关优先级队列数据结构的更多信息,请参阅 https://xlinux.nist.gov/dads//HTML/priorityque.html)。

    heapq 模块提供 heappush()heappop() 函数,用于在保持某些其他可变 Python 序列类型的堆属性的同时添加和删除项目。这是一个使用 Python 列表的示例

    >>> import heapq
    >>> heap = []
    >>> for item in [3, 7, 5, 11, 1]:
    ...    heapq.heappush(heap, item)
    ...
    >>> heap
    [1, 3, 5, 11, 7]
    >>> heapq.heappop(heap)
    1
    >>> heapq.heappop(heap)
    3
    >>> heap
    [5, 7, 11]
    

    (由 Kevin O’Connor 贡献)。

  • IDLE 集成开发环境已使用 IDLEfork 项目中的代码更新 (https://idlefork.sourceforge.net)。最值得注意的特性是,正在开发的代码现在在子进程中执行,这意味着不再需要手动 reload() 操作。IDLE 的核心代码已作为 idlelib 包合并到标准库中。

  • imaplib 模块现在支持通过 SSL 的 IMAP。(由 Piers Lauder 和 Tino Lange 贡献)。

  • itertools 包含许多用于迭代器的有用函数,其灵感来自 ML 和 Haskell 语言提供的各种函数。例如,itertools.ifilter(predicate, iterator) 返回迭代器中函数 predicate() 返回 True 的所有元素,而 itertools.repeat(obj, N) 返回 obj N 次。模块中还有许多其他函数;有关详细信息,请参阅该软件包的参考文档。(由 Raymond Hettinger 贡献)。

  • math 模块中,新增了两个函数:degrees(rads)radians(degs),用于在弧度和角度之间进行转换。 math 模块中的其他函数,例如 math.sin()math.cos(),一直以来都需要以弧度为单位的输入值。此外,还向 math.log() 添加了一个可选的 base 参数,以便更容易计算以 e10 以外的数为底的对数。(由 Raymond Hettinger 贡献。)

  • posix 模块中添加了几个新的 POSIX 函数(getpgid()killpg()lchown()loadavg()major()makedev()minor()mknod()),该模块是 os 模块的基础。(由 Gustavo Niemeyer、Geert Jansen 和 Denis S. Otkidach 贡献。)

  • os 模块中,*stat() 函数系列现在可以在时间戳中报告秒的小数部分。这些时间戳表示为浮点数,类似于 time.time() 返回的值。

    在测试过程中,发现如果时间戳是浮点数,某些应用程序将会崩溃。为了兼容性,当使用 stat_result 的元组接口时,时间戳将表示为整数。当使用命名字段(Python 2.2 中首次引入的功能)时,时间戳仍然表示为整数,除非调用 os.stat_float_times() 来启用浮点返回值。

    >>> os.stat("/tmp").st_mtime
    1034791200
    >>> os.stat_float_times(True)
    >>> os.stat("/tmp").st_mtime
    1034791200.6335014
    

    在 Python 2.4 中,默认设置将更改为始终返回浮点数。

    应用程序开发人员应仅在所有库在面对浮点时间戳时都能正常工作,或者使用元组 API 时,才启用此功能。如果使用该功能,则应在应用程序级别激活该功能,而不是尝试在每次使用时都启用它。

  • optparse 模块包含一个新的命令行参数解析器,它可以将选项值转换为特定的 Python 类型,并自动生成用法消息。有关更多详细信息,请参见以下部分。

  • 旧的且从未记录的 linuxaudiodev 模块已被弃用,并添加了一个名为 ossaudiodev 的新版本。该模块被重命名是因为 OSS 声卡驱动程序可以在 Linux 以外的平台上使用,并且该接口也以各种方式进行了整理和更新。(由 Greg Ward 和 Nicholas FitzRoy-Dale 贡献。)

  • 新的 platform 模块包含许多函数,这些函数尝试确定您正在运行的平台的各种属性。有一些函数可以获取架构、CPU 类型、Windows 操作系统版本,甚至 Linux 发行版版本。(由 Marc-André Lemburg 贡献。)

  • pyexpat 模块提供的解析器对象现在可以选择缓冲字符数据,从而减少对字符数据处理程序的调用次数,从而提高性能。将解析器对象的 buffer_text 属性设置为 True 将启用缓冲。

  • sample(population, k) 函数已添加到 random 模块。population 是一个序列或 xrange 对象,其中包含总体的元素,而 sample() 从总体中选择 k 个元素,而无需替换所选元素。k 可以是任何小于等于 len(population) 的值。例如

    >>> days = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'St', 'Sn']
    >>> random.sample(days, 3)      # Choose 3 elements
    ['St', 'Sn', 'Th']
    >>> random.sample(days, 7)      # Choose 7 elements
    ['Tu', 'Th', 'Mo', 'We', 'St', 'Fr', 'Sn']
    >>> random.sample(days, 7)      # Choose 7 again
    ['We', 'Mo', 'Sn', 'Fr', 'Tu', 'St', 'Th']
    >>> random.sample(days, 8)      # Can't choose eight
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      File "random.py", line 414, in sample
          raise ValueError, "sample larger than population"
    ValueError: sample larger than population
    >>> random.sample(xrange(1,10000,2), 10)   # Choose ten odd nos. under 10000
    [3407, 3805, 1505, 7023, 2401, 2267, 9733, 3151, 8083, 9195]
    

    random 模块现在使用一种新的算法,即用 C 实现的梅森旋转算法。它比以前的算法更快,并且经过更广泛的研究。

    (所有更改均由 Raymond Hettinger 贡献。)

  • readline 模块也获得了一些新函数:get_history_item()get_current_history_length()redisplay()

  • rexecBastion 模块已被宣布为已失效,尝试导入它们将失败并出现 RuntimeError。新型类提供了突破 rexec 提供的受限执行环境的新方法,并且没有人有兴趣修复它们或有时间这样做。如果您的应用程序正在使用 rexec,请将其重写为使用其他替代方案。

    (坚持使用 Python 2.2 或 2.1 并不会使您的应用程序更安全,因为在这些版本的 rexec 模块中存在已知错误。再次强调:如果您正在使用 rexec,请立即停止使用它。)

  • rotor 模块已被弃用,因为它用于加密的算法被认为是不安全的。如果您需要加密,请使用单独提供的几个 AES Python 模块之一。

  • shutil 模块获得了一个 move(src, dest) 函数,该函数以递归方式将文件或目录移动到新位置。

  • signal 添加了对更高级的 POSIX 信号处理的支持,但后来又将其删除,因为它被证明无法在各个平台上可靠地工作。

  • socket 模块现在支持超时。您可以在套接字对象上调用 settimeout(t) 方法来设置 t 秒的超时。随后的套接字操作如果耗时超过 t 秒完成,将会中止并引发 socket.timeout 异常。

    最初的超时实现由 Tim O’Malley 完成。Michael Gilfix 将其集成到 Python 的 socket 模块中,并引导它通过了漫长的审查。代码被检入后,Guido van Rossum 重写了其中的一部分。(这是正在进行的协作开发过程的一个很好的例子。)

  • 在 Windows 上,socket 模块现在附带了安全套接层 (SSL) 支持。

  • C 宏 PYTHON_API_VERSION 的值现在以 sys.api_version 的形式在 Python 级别公开。可以通过调用新的 sys.exc_clear() 函数来清除当前异常。

  • 新的 tarfile 模块允许读取和写入 tar 格式的归档文件。(由 Lars Gustäbel 贡献。)

  • 新的 textwrap 模块包含用于包装包含文本段落的字符串的函数。wrap(text, width) 函数接受一个字符串,并返回一个列表,其中包含文本分割成不超过所选宽度的行。fill(text, width) 函数返回一个字符串,重新格式化以适应不超过所选宽度的行。(正如您可能猜到的,fill() 是基于 wrap() 构建的。例如

    >>> import textwrap
    >>> paragraph = "Not a whit, we defy augury: ... more text ..."
    >>> textwrap.wrap(paragraph, 60)
    ["Not a whit, we defy augury: there's a special providence in",
     "the fall of a sparrow. If it be now, 'tis not to come; if it",
     ...]
    >>> print textwrap.fill(paragraph, 35)
    Not a whit, we defy augury: there's
    a special providence in the fall of
    a sparrow. If it be now, 'tis not
    to come; if it be not to come, it
    will be now; if it be not now, yet
    it will come: the readiness is all.
    >>>
    

    该模块还包含一个 TextWrapper 类,它实际实现了文本换行策略。TextWrapper 类以及 wrap()fill() 函数都支持一些额外的关键字参数,用于微调格式;有关详细信息,请参阅该模块的文档。(由 Greg Ward 贡献。)

  • threadthreading 模块现在有了配套模块,dummy_threaddummy_threading,它们为不支持线程的平台提供了 thread 模块接口的空操作实现。其目的是通过在顶部放置以下代码来简化线程感知模块(那些依赖线程运行的模块)

    try:
        import threading as _threading
    except ImportError:
        import dummy_threading as _threading
    

    在此示例中,_threading 用作模块名称,以明确所使用的模块不一定是实际的 threading 模块。无论是否支持线程,代码都可以调用 _threading 中的函数和使用类,避免使用 if 语句,并使代码更清晰一些。此模块不会神奇地使多线程代码在没有线程的情况下运行;等待另一个线程返回或执行某些操作的代码将永远挂起。

  • time 模块的 strptime() 函数长期以来一直令人烦恼,因为它使用平台 C 库的 strptime() 实现,而不同的平台有时会有奇怪的错误。Brett Cannon 贡献了一个用纯 Python 编写的可移植实现,该实现应在所有平台上表现相同。

  • 新的 timeit 模块有助于测量 Python 代码片段的执行时间。timeit.py 文件可以直接从命令行运行,或者可以导入并直接使用该模块的 Timer 类。这是一个简短的示例,用于确定通过将空 Unicode 字符串附加到 8 位字符串还是使用 unicode() 函数将其转换为 Unicode 更快

    import timeit
    
    timer1 = timeit.Timer('unicode("abc")')
    timer2 = timeit.Timer('"abc" + u""')
    
    # Run three trials
    print timer1.repeat(repeat=3, number=100000)
    print timer2.repeat(repeat=3, number=100000)
    
    # On my laptop this outputs:
    # [0.36831796169281006, 0.37441694736480713, 0.35304892063140869]
    # [0.17574405670166016, 0.18193507194519043, 0.17565798759460449]
    
  • Tix 模块已针对当前版本的 Tix 包进行了各种错误修复和更新。

  • Tkinter 模块现在可以使用启用线程的 Tcl 版本。Tcl 的线程模型要求只能从创建窗口小部件的线程访问窗口小部件;从另一个线程访问可能会导致 Tcl 崩溃。对于某些 Tcl 接口,当从不同的线程访问窗口小部件时,Tkinter 现在会自动避免这种情况,方法是编组一个命令,将其传递到正确的线程,并等待结果。其他接口无法自动处理,但是 Tkinter 现在会在此类访问时引发异常,以便您至少可以找出问题。有关此更改的更详细说明,请参见 https://mail.python.org/pipermail/python-dev/2002-December/031107.html。(由 Martin von Löwis 实现。)

  • 通过 _tkinter 调用 Tcl 方法不再只返回字符串。相反,如果 Tcl 返回其他对象,则这些对象将转换为其 Python 等效项(如果存在),或者如果没有 Python 等效项,则用 _tkinter.Tcl_Obj 对象包装。可以通过 tkapp 对象的 wantobjects() 方法来控制此行为。

    当通过 Tkinter 模块(大多数 Tkinter 应用程序将使用)使用 _tkinter 时,此功能始终处于激活状态。它不应引起兼容性问题,因为 Tkinter 会尽可能将字符串结果转换为 Python 类型。

    如果发现任何不兼容性,可以通过在创建第一个 tkapp 对象之前将 Tkinter 模块中的 wantobjects 变量设置为 false 来恢复旧的行为。

    import Tkinter
    Tkinter.wantobjects = 0
    

    由此更改引起的任何中断都应报告为错误。

  • UserDict 模块有一个新的 DictMixin 类,它为已经具有最小映射接口的类定义了所有字典方法。这大大简化了编写需要替代字典的类(例如 shelve 模块中的类)。

    只要类定义了 __getitem__()__setitem__()__delitem__()keys(),则将 mix-in 作为超类可以提供完整的字典接口。例如

    >>> import UserDict
    >>> class SeqDict(UserDict.DictMixin):
    ...     """Dictionary lookalike implemented with lists."""
    ...     def __init__(self):
    ...         self.keylist = []
    ...         self.valuelist = []
    ...     def __getitem__(self, key):
    ...         try:
    ...             i = self.keylist.index(key)
    ...         except ValueError:
    ...             raise KeyError
    ...         return self.valuelist[i]
    ...     def __setitem__(self, key, value):
    ...         try:
    ...             i = self.keylist.index(key)
    ...             self.valuelist[i] = value
    ...         except ValueError:
    ...             self.keylist.append(key)
    ...             self.valuelist.append(value)
    ...     def __delitem__(self, key):
    ...         try:
    ...             i = self.keylist.index(key)
    ...         except ValueError:
    ...             raise KeyError
    ...         self.keylist.pop(i)
    ...         self.valuelist.pop(i)
    ...     def keys(self):
    ...         return list(self.keylist)
    ...
    >>> s = SeqDict()
    >>> dir(s)      # See that other dictionary methods are implemented
    ['__cmp__', '__contains__', '__delitem__', '__doc__', '__getitem__',
     '__init__', '__iter__', '__len__', '__module__', '__repr__',
     '__setitem__', 'clear', 'get', 'has_key', 'items', 'iteritems',
     'iterkeys', 'itervalues', 'keylist', 'keys', 'pop', 'popitem',
     'setdefault', 'update', 'valuelist', 'values']
    

    (由 Raymond Hettinger 贡献。)

  • xml.dom.minidom 中的 DOM 实现现在可以通过向 DOM 节点的 toxml()toprettyxml() 方法提供可选的编码参数,以特定的编码生成 XML 输出。

  • 现在,xmlrpclib 模块支持 XML-RPC 扩展,用于处理 nil 数据值,例如 Python 的 None。在解组 XML-RPC 响应时,始终支持 nil 值。要生成包含 None 的请求,在创建 Marshaller 实例时,必须为 allow_none 参数提供真值。

  • 新的 DocXMLRPCServer 模块允许编写自文档化的 XML-RPC 服务器。在演示模式下运行它(作为程序)以查看其运行效果。将 Web 浏览器指向 RPC 服务器会生成 pydoc 样式的文档;将 xmlrpclib 指向服务器允许调用实际方法。(由 Brian Quinlan 贡献。)

  • 已添加对国际化域名(RFC 3454、3490、3491 和 3492)的支持。“idna”编码可用于在 Unicode 域名和该名称的 ASCII 兼容编码 (ACE) 之间进行转换。

    >{}>{}> u"www.Alliancefrançaise.nu".encode("idna")
    'www.xn--alliancefranaise-npb.nu'
    

    socket 模块也已扩展为在将 Unicode 主机名传递给 C 库之前,透明地将其转换为 ACE 版本。处理主机名的模块(例如 httplibftplib)也支持 Unicode 主机名;httplib 还使用域名的 ACE 版本发送 HTTP Host 标头。urllib 支持带有非 ASCII 主机名的 Unicode URL,只要 URL 的 path 部分仅包含 ASCII 字符即可。

    为了实现此更改,已添加 stringprep 模块、mkstringprep 工具和 punycode 编码。

日期/时间类型

适合表示时间戳的日期和时间类型已作为 datetime 模块添加。这些类型不支持不同的日历或许多花哨的功能,只是坚持表示时间的基础知识。

三个主要类型是:date,表示日、月和年;time,由小时、分钟和秒组成;以及 datetime,它包含 datetime 的所有属性。还有一个 timedelta 类,表示时间中两个点之间的差异,并且时区逻辑由继承自抽象 tzinfo 类的类实现。

您可以通过向相应的构造函数提供关键字参数来创建 datetime 的实例,例如 datetime.date(year=1972, month=10, day=15),或者使用多个类方法之一。例如,today() 类方法返回当前的本地日期。

创建后,日期/时间类的实例都是不可变的。有许多方法可以从对象生成格式化的字符串

>>> import datetime
>>> now = datetime.datetime.now()
>>> now.isoformat()
'2002-12-30T21:27:03.994956'
>>> now.ctime()  # Only available on date, datetime
'Mon Dec 30 21:27:03 2002'
>>> now.strftime('%Y %d %b')
'2002 30 Dec'

replace() 方法允许修改 datedatetime 实例的一个或多个字段,并返回一个新实例

>>> d = datetime.datetime.now()
>>> d
datetime.datetime(2002, 12, 30, 22, 15, 38, 827738)
>>> d.replace(year=2001, hour = 12)
datetime.datetime(2001, 12, 30, 12, 15, 38, 827738)
>>>

可以比较、哈希实例并将其转换为字符串(结果与 isoformat() 的结果相同)。datedatetime 实例可以彼此相减,并添加到 timedelta 实例。最大的缺失功能是标准库不支持解析字符串并返回 datedatetime

有关更多信息,请参阅模块的参考文档。(由 Tim Peters 贡献。)

optparse 模块

getopt 模块提供了命令行参数的简单解析。新的 optparse 模块(最初名为 Optik)提供了更详细的命令行解析,它遵循 Unix 约定,自动为 --help 创建输出,并且可以为不同的选项执行不同的操作。

首先,创建 OptionParser 的实例,并告诉它程序的选项是什么。

import sys
from optparse import OptionParser

op = OptionParser()
op.add_option('-i', '--input',
              action='store', type='string', dest='input',
              help='set input filename')
op.add_option('-l', '--length',
              action='store', type='int', dest='length',
              help='set maximum length of output')

然后,通过调用 parse_args() 方法来完成命令行解析。

options, args = op.parse_args(sys.argv[1:])
print options
print args

这将返回一个包含所有选项值的对象,以及一个包含其余参数的字符串列表。

现在,使用各种参数调用脚本会按预期工作。请注意,length 参数会自动转换为整数。

$ ./python opt.py -i data arg1
<Values at 0x400cad4c: {'input': 'data', 'length': None}>
['arg1']
$ ./python opt.py --input=data --length=4
<Values at 0x400cad2c: {'input': 'data', 'length': 4}>
[]
$

帮助消息会自动为您生成

$ ./python opt.py --help
usage: opt.py [options]

options:
  -h, --help            show this help message and exit
  -iINPUT, --input=INPUT
                        set input filename
  -lLENGTH, --length=LENGTH
                        set maximum length of output
$

有关更多详细信息,请参阅模块的文档。

Optik 由 Greg Ward 编写,并参考了 Getopt SIG 读者的建议。

Pymalloc:一个专门的对象分配器

Pymalloc 是一个由 Vladimir Marangozov 编写的专门对象分配器,它是添加到 Python 2.1 的一项功能。Pymalloc 的目的是比系统 malloc() 更快,并且对于 Python 程序典型的分配模式具有更少的内存开销。分配器使用 C 的 malloc() 函数来获取大量的内存池,然后从这些池中满足较小的内存请求。

在 2.1 和 2.2 中,pymalloc 是一项实验性功能,默认情况下未启用;您必须在编译 Python 时通过向 configure 脚本提供 --with-pymalloc 选项来显式启用它。在 2.3 中,pymalloc 进行了进一步的增强,现在默认启用;您必须提供 --without-pymalloc 来禁用它。

此更改对于用 Python 编写的代码是透明的;但是,pymalloc 可能会暴露 C 扩展中的错误。C 扩展模块的作者应该在启用 pymalloc 的情况下测试他们的代码,因为一些不正确的代码可能会在运行时导致核心转储。

有一个特别常见的错误会导致问题。Python 的 C API 中有许多内存分配函数,它们以前只是 C 库的 malloc()free() 的别名,这意味着如果您意外地调用了不匹配的函数,则不会注意到错误。启用对象分配器后,这些函数不再是 malloc()free() 的别名,并且调用错误的函数来释放内存可能会导致核心转储。例如,如果内存是使用 PyObject_Malloc() 分配的,则必须使用 PyObject_Free() 而不是 free() 来释放它。Python 附带的几个模块因此而出现问题,并且必须进行修复;毫无疑问,还有更多第三方模块会出现相同的问题。

作为此更改的一部分,用于分配内存的多个令人困惑的接口已合并为两个 API 系列。使用一个系列分配的内存不得使用另一个系列中的函数进行操作。一个系列用于分配内存块,另一个系列专门用于分配 Python 对象。

得益于 Tim Peters 的大量工作,2.3 中的 pymalloc 还提供了调试功能,以捕获扩展模块和解释器本身的内存覆盖和双重释放。要启用此支持,请通过运行带有 --with-pydebugconfigure 来编译 Python 解释器的调试版本。

为了帮助扩展编写者,Python 2.3 的源代码附带了一个头文件 Misc/pymemcompat.h,允许 Python 扩展使用 2.3 内存分配接口,同时针对自 1.5.2 以来的任何 Python 版本进行编译。您需要从 Python 的源代码发行版中复制该文件,并将其与扩展的源代码捆绑在一起。

另请参阅

https://hg.python.org/cpython/file/default/Objects/obmalloc.c

有关 pymalloc 实现的完整详细信息,请参阅 Python 源代码中文件 Objects/obmalloc.c 顶部的注释。上面的链接指向 python.org SVN 浏览器中的文件。

构建和 C API 更改

Python 的构建过程和 C API 的更改包括

  • 垃圾回收使用的循环检测实现已被证明是稳定的,因此现在已设为强制性。您不能再在没有它的情况下编译 Python,并且 configure--with-cycle-gc 开关已删除。

  • 现在,可以通过在运行 Python 的 configure 脚本时提供 --enable-shared,选择性地将 Python 构建为共享库 (libpython2.3.so)。 (由 Ondrej Palkovsky 贡献。)

  • DL_EXPORTDL_IMPORT 宏现在已弃用。Python 扩展模块的初始化函数现在应该使用新的宏 PyMODINIT_FUNC 进行声明,而 Python 核心通常会使用 PyAPI_FUNCPyAPI_DATA 宏。

  • 可以通过向 configure 脚本提供 --without-doc-strings 来编译不包含内置函数和模块的文档字符串的解释器。这会使 Python 可执行文件缩小大约 10%,但这也意味着您无法获得 Python 内置函数的帮助。(由 Gustavo Niemeyer 贡献。)

  • PyArg_NoArgs() 宏现在已弃用,应更改使用它的代码。对于 Python 2.2 及更高版本,方法定义表可以指定 METH_NOARGS 标志,表示没有参数,然后可以删除参数检查。如果与 Python 2.2 之前的版本兼容很重要,则代码可以使用 PyArg_ParseTuple(args, ""),但这会比使用 METH_NOARGS 慢。

  • PyArg_ParseTuple() 接受各种大小的无符号整数的新格式字符:B 代表 unsigned charH 代表 unsigned short intI 代表 unsigned int,以及 K 代表 unsigned long long

  • 添加了一个新函数 PyObject_DelItemString(mapping, char *key),作为 PyObject_DelItem(mapping, PyString_New(key)) 的简写形式。

  • 文件对象现在以不同的方式管理其内部字符串缓冲区,在需要时以指数方式增加它。这导致 Lib/test/test_bufio.py 中的基准测试显着加速(根据一项测量,从 57 秒到 1.7 秒)。

  • 现在可以通过在方法的 PyMethodDef 结构中设置 METH_CLASSMETH_STATIC 标志来为 C 扩展类型定义类方法和静态方法。

  • Python 现在包含 Expat XML 解析器源代码的副本,从而消除了对系统版本或 Expat 本地安装的任何依赖。

  • 如果您在扩展中动态分配类型对象,则应注意与 __module____name__ 属性相关的规则的更改。总而言之,您需要确保类型的字典包含 '__module__' 键;使模块名称成为类型名称中最后一个句点之前的部分将不再具有所需的效果。有关更多详细信息,请阅读 API 参考文档或源代码。

特定于端口的更改

支持使用 EMX 运行时环境移植到 IBM 的 OS/2 的代码已合并到主 Python 源代码树中。EMX 是 OS/2 系统 API 之上的 POSIX 模拟层。EMX 的 Python 端口尝试支持 EMX 运行时公开的所有类似 POSIX 的功能,并且大部分都成功了;fork()fcntl() 受底层模拟层的限制。标准 OS/2 端口(使用 IBM 的 Visual Age 编译器)也获得了对区分大小写的导入语义的支持,作为 EMX 端口集成到 CVS 的一部分。(由 Andrew MacIntyre 贡献。)

在 MacOS 上,大多数工具箱模块都已弱链接,以提高向后兼容性。这意味着如果当前操作系统版本缺少单个例程,模块将不再加载失败。相反,调用缺少的例程将引发异常。(由 Jack Jansen 贡献。)

在 Python 源代码发行版的 Misc/RPM/ 目录中找到的 RPM spec 文件已针对 2.3 更新。(由 Sean Reifschneider 贡献。)

Python 现在支持的其他新平台包括 AtheOS (http://www.atheos.cx/)、GNU/Hurd 和 OpenVMS。

其他更改和修复

与往常一样,整个源代码树中分散着许多其他改进和错误修复。搜索 CVS 更改日志发现,在 Python 2.2 和 2.3 之间应用了 523 个补丁并修复了 514 个错误。这两个数字都可能被低估了。

一些更值得注意的更改是

  • 如果设置了 PYTHONINSPECT 环境变量,则 Python 解释器将在运行 Python 程序后进入交互式提示符,就像使用 -i 选项调用 Python 一样。可以在运行 Python 解释器之前设置环境变量,也可以由 Python 程序在执行过程中设置。

  • regrtest.py 脚本现在提供了一种允许“除 *foo* 之外的所有资源”的方法。传递给 -u 选项的资源名称现在可以用连字符 ('-') 前缀表示“删除此资源”。例如,选项 ‘-uall,-bsddb’ 可用于启用除 bsddb 之外的所有资源。

  • 用于构建文档的工具现在可以在 Cygwin 以及 Unix 下工作。

  • 已移除 SET_LINENO 操作码。在遥远的过去,需要此操作码在回溯中生成行号并支持跟踪函数(例如,对于 pdb)。自 Python 1.5 以来,回溯中的行号已使用不同的机制计算,该机制可与“python -O”一起使用。对于 Python 2.3,Michael Hudson 实现了一个类似的方案来确定何时调用跟踪函数,完全消除了对 SET_LINENO 的需求。

    很难从 Python 代码中检测到任何由此产生的差异,除了在不使用 -O 运行 Python 时速度会略有提升。

    访问帧对象的 f_lineno 字段的 C 扩展应改为调用 PyCode_Addr2Line(f->f_code, f->f_lasti)。这将带来额外的效果,使代码在早期版本的 Python 中在“python -O”下按预期工作。

    一个很棒的新特性是,跟踪函数现在可以赋值给帧对象的 f_lineno 属性,从而更改接下来将要执行的行。一个 jump 命令已添加到 pdb 调试器中,利用了这个新特性。(由 Richie Hindle 实现。)

移植到 Python 2.3

本节列出了之前描述的可能需要更改代码的更改

  • yield 现在始终是一个关键字;如果它在您的代码中用作变量名,则必须选择不同的名称。

  • 对于字符串 XY,如果 X 的长度超过一个字符,则 X in Y 现在可以工作。

  • 当字符串或浮点数太大而无法放入整数时,int() 类型构造函数现在将返回长整数,而不是引发 OverflowError

  • 如果您的 Unicode 字符串包含 8 位字符,您必须通过在文件顶部添加注释来声明文件的编码(UTF-8、Latin-1 或其他编码)。有关详细信息,请参阅 PEP 263:源代码编码 部分。

  • 通过 _tkinter 调用 Tcl 方法不再仅返回字符串。相反,如果 Tcl 返回其他对象,则这些对象会被转换为其 Python 等效项(如果存在),或者如果没有 Python 等效项,则会被包装在 _tkinter.Tcl_Obj 对象中。

  • 大型八进制和十六进制字面量(例如 0xffffffff)现在会触发 FutureWarning。目前,它们存储为 32 位数字并导致负值,但在 Python 2.4 中,它们将变为正长整数。

    有几种方法可以解决此警告。如果您真的需要一个正数,只需在字面量末尾添加一个 L。如果您尝试获取设置了低位的 32 位整数,并且之前使用过诸如 ~(1 << 31) 之类的表达式,那么最清楚的方法可能是从所有位都设置开始,并清除所需的上位。例如,要清除最高位(第 31 位),您可以编写 0xffffffffL &~(1L<<31)

  • 您不再可以通过赋值给 __debug__ 来禁用断言。

  • Distutils setup() 函数获得了各种新的关键字参数,例如 depends。如果传递了未知的关键字,旧版本的 Distutils 将中止。一种解决方案是检查您的 setup.py 中是否存在新的 get_distutil_options() 函数,并且仅在支持它们的 Distutils 版本中使用新的关键字

    from distutils import core
    
    kw = {'sources': 'foo.c', ...}
    if hasattr(core, 'get_distutil_options'):
        kw['depends'] = ['foo.h']
    ext = Extension(**kw)
    
  • None 用作变量名现在将导致 SyntaxWarning 警告。

  • Python 附带的模块定义的扩展类型的名称现在包含模块和一个 '.' 在类型名称之前。

致谢

作者要感谢以下人士为本文的各种草稿提供建议、更正和帮助:Jeff Bauer、Simon Brunning、Brett Cannon、Michael Chermside、Andrew Dalke、Scott David Daniels、Fred L. Drake, Jr.、David Fraser、Kelly Gerber、Raymond Hettinger、Michael Hudson、Chris Lambert、Detlef Lannert、Martin von Löwis、Andrew MacIntyre、Lalo Martins、Chad Netzer、Gustavo Niemeyer、Neal Norwitz、Hans Nowak、Chris Reedy、Francesco Ricciardi、Vinay Sajip、Neil Schemenauer、Roman Suzi、Jason Tishler、Just van Rossum。