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 - 添加内置集合对象类型
PEP 由 Greg V. Wilson 撰写。由 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
语句。yield
和 return
语句之间的最大区别在于,到达 yield
时,生成器的执行状态被挂起,局部变量被保留。在下一次调用生成器的 .next()
方法时,函数将立即在 yield
语句之后恢复执行。(由于复杂的原因,yield
语句不允许在 try
...finally
语句的 try
块内;请阅读 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 中,每个表达式和函数调用都像生成器一样工作。来自 https://www2.cs.arizona.edu/icon/docs/ipd266.htm 的“Icon 编程语言概述”中的一个示例给出了它的样子
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 的 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
都由一个名称标识,名称使用 .
作为组件分隔符组织成一个层次结构。例如,您可能拥有名为 server
、server.auth
和 server.network
的 Logger
实例。后两个实例在层次结构中位于 server
之下。这意味着如果您提高 server
的详细程度或将 server
消息定向到不同的处理程序,这些更改也将应用于记录到 server.auth
和 server.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
的消息也会被 server
和 root
看到,但 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__
模块中,True
和 False
。(True
和 False
常量已在 Python 2.2.1 中添加到内置函数中,但 2.2.1 版本只是简单地设置为整数值 1 和 0,并不是不同的类型。)
此新类型的类型对象名为 bool
;它的构造函数接受任何 Python 值并将其转换为 True
或 False
。
>>> 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
一句话总结 True
和 False
:它们是整数值 1 和 0 的替代拼写方式,唯一的区别是 str()
和 repr()
返回字符串 'True'
和 'False'
而不是 '1'
和 '0'
。
参见
- PEP 285 - 添加 bool 类型
由 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 获得。
为了使目录更有用,已向 Distutils setup()
函数添加了一个新的可选 *classifiers* 关键字参数。可以提供一个 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
模块以来,就可以编写自定义导入钩子,但没有人真正满意,因为编写新的导入钩子既困难又混乱。曾有各种替代方案被提出,例如 imputil
和 iu
模块,但它们都没有得到广泛接受,而且它们都无法轻松地从 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* 将是模块或包名称,例如 string
或 distutils.core
。find_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 增强功能¶
pickle
和 cPickle
模块在 2.3 开发周期中受到了一些关注。在 2.2 中,新式类可以轻松地进行 pickle,但它们 pickle 得不够紧凑;PEP 307 引用了一个简单的示例,其中新式类导致 pickle 字符串的长度是经典类的三倍。
解决方案是发明一种新的 pickle 协议。pickle.dumps()
函数长期以来一直支持文本或二进制标志。在 2.3 中,此标志从布尔值重新定义为整数:0 是旧的文本模式 pickle 格式,1 是旧的二进制格式,现在 2 是新的 2.3 特有格式。一个新的常量 pickle.HIGHEST_PROTOCOL
可以用来选择可用的最复杂的协议。
解包不再被认为是安全操作。2.2 的 pickle
提供了尝试阻止不安全类被解包的钩子(特别是 __safe_for_unpickling__
属性),但这些代码从未经过审计,因此它们在 2.3 中都被删除了。您不应在任何版本的 Python 中解包不受信任的数据。
为了减少新式类的 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)
,它给定序列的长度,返回一个 (start, stop, step)
元组,可以直接传递给 range()
。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 保持一致,其中 int
、str
等也经历了相同的变化。
其他语言更改¶
以下是 Python 2.3 对核心 Python 语言进行的所有更改。
yield
语句现在始终是关键字,如本文档的 PEP 255: Simple Generators 一节所述。添加了一个新的内置函数
enumerate()
,如本文档的 PEP 279: enumerate() 一节所述。新增了两个常量,
True
和False
,以及内置的bool
类型,如本文档的 PEP 285: A Boolean Type 一节所述。int()
类型构造函数现在将返回一个长整数,而不是在字符串或浮点数太大而无法适应整数时引发OverflowError
。这可能导致isinstance(int(expression), int)
为假的自相矛盾的结果,但这在实践中似乎不太可能引起问题。内置类型现在支持扩展切片语法,如本文档 扩展切片 一节所述。
新增了一个内置函数
sum(iterable, start=0)
,它将可迭代对象中的数字项相加并返回它们的总和。sum()
只接受数字,这意味着您不能用它来连接一堆字符串。(由 Alex Martelli 贡献。)list.insert(pos, value)
过去在 *pos* 为负数时将 *value* 插入到列表的开头。现在行为已更改为与切片索引一致,因此当 *pos* 为 -1 时,值将插入到倒数第二个元素之前,依此类推。list.index(value)
,它在列表中搜索 *value* 并返回其索引,现在接受可选的 *start* 和 *stop* 参数,以将搜索限制在列表的一部分。字典有一个新方法
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 贡献。)
assert
语句不再检查__debug__
标志,因此您无法再通过赋值给__debug__
来禁用断言。使用-O
开关运行 Python 仍然会生成不执行任何断言的代码。大多数类型对象现在都是可调用的,因此您可以使用它们创建新对象,例如函数、类和模块。(这意味着
new
模块在未来的 Python 版本中可能会被弃用,因为您现在可以使用types
模块中可用的类型对象。)例如,您可以使用以下代码创建新的模块对象>>> import types >>> m = types.ModuleType('abc','docstring') >>> m <module 'abc' (built-in)> >>> m.__doc__ 'docstring'
添加了一个新警告
PendingDeprecationWarning
,用于指示正在弃用的功能。默认情况下,该警告将不会打印。要检查未来将弃用的功能的使用情况,请在命令行中提供-Walways::PendingDeprecationWarning::
或使用warnings.filterwarnings()
。基于字符串的异常(例如
raise "Error occurred"
)的弃用过程已经开始。现在引发字符串将触发PendingDeprecationWarning
。将
None
用作变量名现在将导致SyntaxWarning
警告。在未来的 Python 版本中,None
最终可能会成为一个关键字。Python 2.1 中引入的文件对象的
xreadlines()
方法不再必要,因为文件现在可以作为自己的迭代器。xreadlines()
最初是为了更快地遍历文件中的所有行而引入的,但现在您可以简单地编写for line in file_obj
。文件对象还有一个新的只读encoding
属性,它给出文件使用的编码;写入文件的 Unicode 字符串将使用给定编码自动转换为字节。新式类使用的方法解析顺序已更改,尽管您只有在拥有真正复杂的继承层次结构时才会注意到差异。经典类不受此更改的影响。Python 2.2 最初使用类的祖先的拓扑排序,但 2.3 现在使用论文 “A Monotonic Superclass Linearization for Dylan” 中描述的 C3 算法。要了解此更改的动机,请阅读 Michele Simionato 的文章 Python 2.3 方法解析顺序,或阅读 python-dev 上的线程,从 https://mail.python.org/pipermail/python-dev/2002-October/029035.html 的消息开始。Samuele Pedroni 首先指出了问题并通过编写 C3 算法实现了修复。
Python 通过在执行 N 个字节码后切换线程来运行多线程程序。N 的默认值已从 10 增加到 100 个字节码,通过减少切换开销来加速单线程应用程序。一些多线程应用程序可能会遇到较慢的响应时间,但这很容易通过使用
sys.setcheckinterval(N)
将限制设置回较低的数字来解决。可以使用新的sys.getcheckinterval()
函数检索该限制。一个微小但影响深远的改变是,Python 随附模块定义的扩展类型的名称现在包含模块名和类型名前的
'.'
。例如,在 Python 2.2 中,如果你创建一个套接字并打印它的__class__
,你会得到以下输出>>> s = socket.socket() >>> s.__class__ <type 'socket'>
在 2.3 中,你得到这个
>>> s.__class__ <type '_socket.socket'>
旧式和新式类之间的一个显著不兼容性已被移除:您现在可以为新式类的
__name__
和__bases__
属性赋值。对__bases__
的赋值有一些限制,类似于与实例的__class__
属性相关的限制。
字符串更改¶
in
运算符现在对字符串有不同的工作方式。以前,当评估X in Y
(其中 *X* 和 *Y* 是字符串)时,*X* 只能是单个字符。现在已更改;*X* 可以是任意长度的字符串,并且如果 *X* 是 *Y* 的子字符串,X in Y
将返回True
。如果 *X* 是空字符串,结果总是True
。>>> 'ab' in 'abcd' True >>> 'ad' in 'abcd' False >>> '' in 'abcd' True
请注意,这不会告诉您子字符串从何处开始;如果您需要该信息,请使用
find()
字符串方法。strip()
、lstrip()
和rstrip()
字符串方法现在有一个可选参数,用于指定要剥离的字符。默认仍然是删除所有空白字符>>> ' abc '.strip() 'abc' >>> '><><abc<><><>'.strip('<>') 'abc' >>> '><><abc<><><>\n'.strip('<>') 'abc<><><>\n' >>> u'\u4000\u4001abc\u4000'.strip(u'\u4000') u'\u4001abc' >>>
(由 Simon Brunning 建议并由 Walter Dörwald 实现。)
startswith()
和endswith()
字符串方法现在接受负数作为 *start* 和 *end* 参数。另一个新的字符串方法是
zfill()
,它最初是string
模块中的一个函数。zfill()
用左侧的零填充数字字符串,直到达到指定的宽度。请注意,%
运算符仍然比zfill()
更灵活和强大。>>> '45'.zfill(4) '0045' >>> '12345'.zfill(4) '12345' >>> 'goofy'.zfill(6) '0goofy'
(由 Walter Dörwald 贡献。)
添加了一个新类型对象
basestring
。8 位字符串和 Unicode 字符串都继承自此类型,因此isinstance(obj, basestring)
对于这两种字符串都将返回True
。它是一个完全抽象的类型,因此您无法创建basestring
实例。内部化字符串不再是永恒的,当它们唯一的引用来自内部化字符串的内部字典时,它们现在会以通常的方式进行垃圾回收。(由 Oren Tirosh 实现。)
优化¶
新式类实例的创建速度得到了大幅提升;现在它们比经典类更快!
列表对象的
sort()
方法已被 Tim Peters 大量重写,实现速度显着提高。由于卡拉楚巴乘法(Karatsuba multiplication)的实现,大长整数的乘法现在快得多,这是一种比学校乘法算法所需的 *O*(n2) 更好地扩展的算法。(原始补丁由 Christopher A. Craig 贡献,并由 Tim Peters 大量修改。)
SET_LINENO
操作码现在已消失。这可能会提供一个小小的速度提升,具体取决于您的编译器的特性。有关更长的解释,请参阅 其他更改和修复 一节。(由 Michael Hudson 移除。)xrange()
对象现在有自己的迭代器,使得for i in xrange(n)
略快于for i in range(n)
。(由 Raymond Hettinger 贡献的补丁。)在各种热点中进行了一些小的重新排列以提高性能,例如内联函数或删除一些代码。(主要由 GvR 实现,但许多人都贡献了单次更改。)
2.3 优化结果是 Python 2.3 运行 pystone 基准测试比 Python 2.2 快约 25%。
新的、改进的和已弃用的模块¶
一如既往,Python 的标准库获得了一些增强和错误修复。以下是按模块名称字母顺序排列的最显著更改的部分列表。有关更完整的更改列表,请查阅源代码树中的 Misc/NEWS
文件,或查看 CVS 日志以获取所有详细信息。
array
模块现在使用'u'
格式字符支持 Unicode 字符数组。数组现在还支持使用+=
赋值运算符添加另一个数组的内容,以及使用*=
赋值运算符重复数组。(由 Jason Orendorff 贡献。)bsddb
模块已被 PyBSDDB 包的 4.1.6 版替换,它为 BerkeleyDB 库的事务功能提供了更完整的接口。旧版本的模块已重命名为
bsddb185
,不再自动构建;您需要编辑Modules/Setup
以启用它。请注意,新的bsddb
包旨在与旧模块兼容,因此如果发现任何不兼容之处,请务必提交错误报告。将 Python 升级到 2.3 时,如果新的解释器是使用底层 BerkeleyDB 库的新版本编译的,您几乎肯定需要将数据库文件转换为新版本。您可以使用新的脚本db2pickle.py
和pickle2db.py
相当轻松地完成此操作,这些脚本位于发行版的Tools/scripts
目录中。如果您已经在使用 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 的其他次要更改:它现在检查
CC
、CFLAGS
、CPP
、LDFLAGS
和CPPFLAGS
环境变量,并使用它们覆盖 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 贡献。)
grp
、pwd
和resource
模块现在返回增强的元组>>> import grp >>> g = grp.getgrnam('amk') >>> g.gr_name, g.gr_gid ('amk', 500)
gzip
模块现在可以处理超过 2 GiB 的文件。新的
heapq
模块包含堆队列算法的实现。堆是一种类似数组的数据结构,它以部分排序的方式存储项,使得对于每个索引 k,heap[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 参数,以便更容易计算非e
和10
为底的对数。(由 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
模块现在使用 Mersenne Twister 算法(用 C 实现)。它比以前的算法更快,并且经过了更广泛的研究。(所有更改均由 Raymond Hettinger 贡献。)
readline
模块还新增了几个函数:get_history_item()
、get_current_history_length()
和redisplay()
。rexec
和Bastion
模块已被宣布废弃,尝试导入它们将因RuntimeError
而失败。新式类提供了新的方法来突破rexec
提供的受限执行环境,并且没有人有兴趣或时间来修复它们。如果您的应用程序使用rexec
,请将其重写以使用其他东西。(坚持使用 Python 2.2 或 2.1 不会使您的应用程序更安全,因为这些版本中的
rexec
模块存在已知错误。重申:如果您正在使用rexec
,请立即停止使用它。)rotor
模块已被弃用,因为其用于加密的算法被认为不安全。如果需要加密,请使用单独提供的几个 AES Python 模块之一。shutil
模块新增了一个move(src, dest)
函数,该函数递归地将文件或目录移动到新位置。对更高级的 POSIX 信号处理的支持已添加到
signal
中,但后来又被删除,因为它被证明无法在不同平台上可靠地工作。socket
模块现在支持超时。您可以在 socket 对象上调用settimeout(t)
方法来设置 t 秒的超时。后续的 socket 操作如果完成时间超过 t 秒,将中止并引发socket.timeout
异常。最初的超时实现由 Tim O’Malley 完成。Michael Gilfix 将其集成到 Python
socket
模块中,并经过了漫长的审查。代码提交后,Guido van Rossum 对其部分进行了重写。(这是一个协作开发过程的良好示例。)在 Windows 上,
socket
模块现在随附 Secure Sockets Layer (SSL) 支持。C
PYTHON_API_VERSION
宏的值现在在 Python 级别公开为sys.api_version
。可以通过调用新的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 贡献。)thread
和threading
模块现在有了配套模块dummy_thread
和dummy_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
类可以直接导入和使用。这是一个简短的示例,用于判断是将 8 位字符串转换为 Unicode 更快,是通过在其后附加一个空 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 的线程模型要求只能从创建 widget 的线程访问 widget;从另一个线程访问可能导致 Tcl 崩溃。对于某些 Tcl 接口,Tkinter
现在将在从不同线程访问 widget 时,通过编组命令、将其传递给正确的线程并等待结果来自动避免此问题。其他接口无法自动处理,但Tkinter
现在将在此类访问时引发异常,以便您至少可以发现问题。有关此更改的更详细解释,请参阅 https://mail.python.org/pipermail/python-dev/2002-December/031107.html。(由 Martin von Löwis 实现。)通过
_tkinter
调用 Tcl 方法不再只返回字符串。相反,如果 Tcl 返回其他对象,这些对象将被转换为其 Python 等效项(如果存在),或者如果没有 Python 等效项,则用_tkinter.Tcl_Obj
对象包装。当通过
Tkinter
模块使用_tkinter
时(大多数 Tkinter 应用程序都会如此),此功能始终处于激活状态。它不应导致兼容性问题,因为 Tkinter 始终会在可能的情况下将字符串结果转换为 Python 类型。如果发现任何不兼容性,可以通过在创建第一个
tkapp
对象之前,将Tkinter
模块中的wantobjects
变量设置为 false 来恢复旧行为。import Tkinter Tkinter.wantobjects = 0
由此更改引起的任何中断都应报告为错误。
UserDict
模块有一个新的DictMixin
类,它为已经具有最小映射接口的类定义了所有字典方法。这大大简化了编写需要替换字典的类,例如shelve
模块中的类。只要类定义了
__getitem__()
、__setitem__()
、__delitem__()
和keys()
,将混入作为超类可提供完整的字典接口。例如>>> 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 版本。处理主机名的模块(例如httplib
和ftplib
)也支持 Unicode 主机名;httplib
还使用域名的 ACE 版本发送 HTTPHost
头。urllib
支持带有非 ASCII 主机名的 Unicode URL,只要 URL 的path
部分仅为 ASCII。为了实现这一变化,新增了
stringprep
模块、mkstringprep
工具和punycode
编码。
日期/时间类型¶
适合表示时间戳的日期和时间类型已作为 datetime
模块添加。这些类型不支持不同的日历或许多花哨的功能,仅限于表示时间的基本功能。
三个主要类型是:date
,表示日、月和年;time
,由时、分和秒组成;以及 datetime
,它包含 date
和 time
的所有属性。还有一个 timedelta
类,表示两个时间点之间的差异,时区逻辑由继承自抽象 tzinfo
类的类实现。
您可以创建 date
和 time
的实例,可以通过向相应的构造函数提供关键字参数,例如 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()
方法允许修改 date
或 datetime
实例的一个或多个字段,返回一个新实例
>>> 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()
的结果相同)。date
和 datetime
实例可以相互减去,并添加到 timedelta
实例。最大的缺失功能是没有标准库支持解析字符串并返回 date
或 datetime
。
有关更多信息,请参阅模块的参考文档。(由 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 对象。
要分配和释放不区分的内存块,请使用“原始内存”系列:
PyMem_Malloc()
、PyMem_Realloc()
和PyMem_Free()
。“对象内存”系列是上述 pymalloc 功能的接口,偏向于大量“小”分配:
PyObject_Malloc()
、PyObject_Realloc()
和PyObject_Free()
。要分配和释放 Python 对象,请使用“对象”系列
PyObject_New
、PyObject_NewVar
和PyObject_Del()
。
多亏了 Tim Peters 的大量工作,2.3 中的 pymalloc 还提供了调试功能,可以在扩展模块和解释器本身中捕获内存覆盖和双重释放。要启用此支持,请通过运行 configure 并使用 --with-pydebug
来编译 Python 解释器的调试版本。
为了帮助扩展编写者,Python 2.3 源代码随附了一个头文件 Misc/pymemcompat.h
,允许 Python 扩展在使用 Python 1.5.2 以来的任何版本编译时使用 2.3 接口进行内存分配。您可以从 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_EXPORT
和DL_IMPORT
宏现在已弃用。Python 扩展模块的初始化函数现在应使用新的宏PyMODINIT_FUNC
声明,而 Python 核心通常将使用PyAPI_FUNC
和PyAPI_DATA
宏。解释器可以在不包含内置函数和模块的任何文档字符串的情况下编译,方法是向 configure 脚本提供
--without-doc-strings
。这会使 Python 可执行文件缩小约 10%,但也意味着您无法获取 Python 内置函数的帮助。(由 Gustavo Niemeyer 贡献。)PyArg_NoArgs()
宏现在已弃用,使用它的代码应进行更改。对于 Python 2.2 及更高版本,方法定义表可以指定METH_NOARGS
标志,表示没有参数,然后可以删除参数检查。如果与 2.2 之前的 Python 版本兼容很重要,代码可以使用PyArg_ParseTuple(args, "")
代替,但这会比使用METH_NOARGS
慢。PyArg_ParseTuple()
接受用于各种大小的无符号整数的新格式字符:B
代表 unsigned char,H
代表 unsigned short int,I
代表 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_CLASS
或METH_STATIC
标志来为 C 扩展类型定义类方法和静态方法。Python 现在包含了 Expat XML 解析器的源代码副本,消除了对系统版本或本地 Expat 安装的任何依赖。
如果您在扩展中动态分配类型对象,则应该注意与
__module__
和__name__
属性相关的规则发生了变化。简而言之,您将需要确保类型的字典包含'__module__'
键;将模块名称作为类型名称中最后一个句点之前的部分将不再产生预期的效果。有关更多详细信息,请阅读 API 参考文档或源代码。
特定于端口的更改¶
对 IBM OS/2 使用 EMX 运行时环境的移植支持已合并到主 Python 源代码树中。EMX 是 OS/2 系统 API 之上的 POSIX 仿真层。EMX 的 Python 移植尝试支持 EMX 运行时公开的所有类似 POSIX 的功能,并且大部分成功;fork()
和 fcntl()
受底层仿真层的限制。标准 OS/2 移植(使用 IBM 的 Visual Age 编译器)也获得了对大小写敏感导入语义的支持,作为 EMX 移植集成到 CVS 的一部分。(由 Andrew MacIntyre 贡献。)
在 MacOS 上,大多数工具箱模块已被弱链接以提高向后兼容性。这意味着如果当前操作系统版本缺少单个例程,模块将不再无法加载。相反,调用缺失的例程将引发异常。(由 Jack Jansen 贡献。)
RPM 规范文件,位于 Python 源代码发行版中的 Misc/RPM/
目录中,已针对 2.3 进行了更新。(由 Sean Reifschneider 贡献。)
Python 现在支持的其他新平台包括 AtheOS(http://www.atheos.cx/)、GNU/Hurd 和 OpenVMS。
其他更改和修复¶
像往常一样,整个源代码树中散布着许多其他改进和错误修复。搜索 CVS 更改日志发现,在 Python 2.2 和 2.3 之间应用了 523 个补丁,修复了 514 个错误。这两个数字可能都被低估了。
一些更显著的变化是
如果设置了
PYTHONINSPECT
环境变量,Python 解释器将在运行 Python 程序后进入交互式提示符,就像 Python 是使用-i
选项调用一样。环境变量可以在运行 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
属性,从而更改将要执行的下一行。pdb
调试器已添加一个jump
命令,利用了此新功能。(由 Richie Hindle 实现。)
移植到 Python 2.3¶
本节列出了可能需要更改代码的先前描述的更改
yield
现在始终是一个关键字;如果它在您的代码中用作变量名,则必须选择不同的名称。对于字符串 X 和 Y,如果 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。