Python 2.3 的新特性 — Python 文档
Python 2.3 中的新功能
- 作者
- 是 库克林
本文解释了 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 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 语句之后立即恢复执行。 (出于复杂的原因,try…finally 语句的 try 块中不允许使用 yield 语句;阅读 [X154X ]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-Queens 问题(将 $N$ 个皇后放在 $NxN$ 棋盘上,这样没有皇后威胁另一个)和骑士之旅(一条将一个骑士带到$NxN$ 棋盘的每个方格,而无需两次访问任何方格)。
生成器的思想来自其他编程语言,尤其是 Icon (https://www.cs.arizona.edu/icon/),其中生成器的思想是核心。 在 Icon 中,每个表达式和函数调用都像一个生成器。 https://www.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()
函数返回找到子字符串“或”的索引: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 档案导入模块
- 由詹姆斯 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、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'
模式打开文件将在 通用换行符 模式下打开文件进行读取。 所有三个行结束约定都将在由各种文件方法(例如 read()
和 readline())返回的字符串中转换为 '\n'
。
导入模块和使用 execfile() 函数执行文件时,也使用通用换行支持。 这意味着 Python 模块可以在所有三个操作系统之间共享,而无需转换行尾。
通过在运行 Python 的 configure 脚本时指定 --without-universal-newlines
开关,可以在编译 Python 时禁用此功能。
PEP 279:枚举()
新的内置函数 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 282:日志包
用于编写日志的标准包 logging 已添加到 Python 2.3。 它提供了一种强大而灵活的机制来生成日志输出,然后可以以各种方式对其进行过滤和处理。 以标准格式编写的配置文件可用于控制程序的日志记录行为。 Python 包括将日志记录写入标准错误或文件或套接字的处理程序,将它们发送到系统日志,甚至通过电子邮件将它们发送到特定地址; 当然,也可以编写自己的处理程序类。
Logger
类是主要类。 大多数应用程序代码将处理一个或多个 Logger
对象,每个对象都由应用程序的特定子系统使用。 每个 Logger
由一个名称标识,名称使用 .
作为组件分隔符组织成层次结构。 例如,您可能有名为 server
、server.auth
和 server.network
的 Logger
实例。 后两个实例在层次结构中低于 server
。 这意味着,如果您提高 server
的详细程度或将 server
消息定向到不同的处理程序,则更改也将应用于记录到 server.auth
和 [ X184X]。 还有一个根 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 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 语句中使用任何表达式,即使是计算结果为列表或元组或某些随机对象的表达式。 Boolean 类型是 int 类的子类,因此使用 Boolean 的算术仍然有效。
>>> True + 1
2
>>> False + 1
1
>>> False * 75
0
>>> True * 75
75
总结 True 和 False 一句话:它们是拼写整数值 1 和 0 的替代方法,唯一的区别是 str()和 repr() 返回字符串 'True'
和 'False'
而不是 '1'
和 '0'
。
PEP 293:编解码器错误处理回调
将 Unicode 字符串编码为字节字符串时,可能会遇到无法编码的字符。 到目前为止,Python 允许将错误处理指定为“严格”(提高 UnicodeError
)、“忽略”(跳过字符)或“替换”(在输出字符串中使用问号),其中“严格”是默认行为。 可能需要指定此类错误的替代处理,例如将 XML 字符引用或 HTML 实体引用插入到转换后的字符串中。
Python 现在有一个灵活的框架来添加不同的处理策略。 可以使用 codecs.register_error() 添加新的错误处理程序,然后编解码器可以使用 codecs.lookup_error() 访问错误处理程序。 为用 C 编写的编解码器添加了等效的 C API。 错误处理程序获取必要的状态信息,例如正在转换的字符串、在字符串中检测到错误的位置以及目标编码。 然后处理程序可以引发异常或返回替换字符串。
使用此框架实现了两个额外的错误处理程序:“backslashreplace”使用 Python 反斜杠引用来表示不可编码的字符,“xmlcharrefreplace”发出 XML 字符引用。
PEP 301:Distutils 的包索引和元数据
对长期请求的 Python 目录的支持在 2.3 中首次出现。
目录的核心是新的 Distutils register 命令。 跑步python setup.py register
将收集描述包的元数据,如名称、版本、维护者、描述等,并将其发送到中央目录服务器。 生成的目录可从 https://pypi.org 获得。
为了使目录更有用,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 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 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 和 cPickle 模块在 2.3 开发周期中受到了一些关注。 在 2.2 中,新式类可以毫无困难地被腌制,但它们的腌制不是很紧凑; PEP 307 引用了一个简单的例子,在这个例子中,新式类产生的腌制字符串是经典类的三倍。
解决方案是发明一种新的泡菜协议。 pickle.dumps() 函数长期以来一直支持文本或二进制标志。 在 2.3 中,此标志从布尔值重新定义为整数:0 是旧的文本模式 pickle 格式,1 是旧的二进制格式,现在 2 是新的 2.3 特定格式。 一个新的常量 pickle.HIGHEST_PROTOCOL 可用于选择可用的最佳协议。
脱酸不再被视为安全操作。 2.2 的 pickle 提供了用于防止不安全类被 unpickle 的钩子(特别是一个 __safe_for_unpickling__
属性),但是这些代码都没有经过审计,因此都被删除了2.3. 你不应该在任何版本的 Python 中解压不受信任的数据。
为了减少新样式类的酸洗开销,使用三个特殊方法添加了用于自定义酸洗的新接口:__getstate__()
、__setstate__()
和 __getnewargs__()
。 有关这些方法的完整语义,请参阅 PEP 307。
作为进一步压缩泡菜的一种方法,现在可以使用整数代码而不是长字符串来识别泡菜类。 Python 软件基金会将维护一份标准化代码列表; 还有一系列供私人使用的代码。 目前没有指定代码。
扩展切片
从 Python 1.4 开始,切片语法就支持可选的第三个“step”或“stride”参数。 例如,这些都是合法的 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:布尔类型 [ X158X] 本文档。
int() 类型构造函数现在将返回一个长整数,而不是在字符串或浮点数太大而无法放入整数时引发
OverflowError
。 这可能会导致isinstance(int(expression), int)
为假的矛盾结果,但这似乎不太可能在实践中引起问题。内置类型现在支持扩展切片语法,如本文档的 扩展切片 部分所述。
一个新的内置函数
sum(iterable, start=0)
将可迭代对象中的数字项相加并返回它们的总和。 sum() 只接受数字,这意味着你不能用它来连接一堆字符串。 (亚历克斯·马泰利供稿。)list.insert(pos, value)
用于当pos为负数时,在列表前面插入值。 该行为现在已更改为与切片索引一致,因此当 pos 为 -1 时,该值将插入到最后一个元素之前,依此类推。list.index(value)
在列表中搜索 value 并返回其索引,现在采用可选的 start 和 stop 参数来限制搜索名单的一部分。字典有一个新方法
pop(key[, *default*])
,它返回对应于 key 的值并从字典中删除该键/值对。 如果请求的键不存在于字典中,则返回 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 现在使用 C3 算法,如论文 “Dylan 的单调超类线性化” 中所述。 要了解此更改的动机,请阅读 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__ 属性。 根据与分配给实例的 __class__ 属性相关的内容,可以分配给 __bases__ 的内容有一些限制。
字符串更改
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 乘法的实现,大长整数的乘法现在要快得多,该算法的扩展性比小学乘法算法所需的 O(n*n) 更好。 (Christopher A. 的原始补丁。 克雷格,并由蒂姆彼得斯进行了重大改造。)
SET_LINENO
操作码现已消失。 这可能会稍微提高速度,具体取决于您的编译器的特性。 有关详细说明,请参阅 其他更改和修复 部分。 (由迈克尔·哈德森删除。)- xrange() 对象现在有自己的迭代器,使
for i in xrange(n)
比for i in range(n)
略快。 (雷蒙德·赫廷格的补丁。) - 在各种热点中进行了许多小的重新排列以提高性能,例如内联函数或删除一些代码。 (主要由 GvR 实现,但很多人贡献了单个更改。)
The net result of the 2.3 optimizations is that Python 2.3 runs the pystone benchmark around 25% faster than Python 2.2.
新的、改进的和弃用的模块
像往常一样,Python 的标准库得到了许多增强和错误修复。 这是最显着更改的部分列表,按模块名称的字母顺序排序。 请查阅源代码树中的 Misc/NEWS
文件以获取更完整的更改列表,或查看 CVS 日志以获取所有详细信息。
array 模块现在支持使用
'u'
格式字符的 Unicode 字符数组。 数组现在还支持使用+=
赋值运算符来添加另一个数组的内容,并使用*=
赋值运算符来重复数组。 (由杰森·奥伦多夫提供。)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 压缩数据。 (古斯塔沃·尼迈耶供稿。)
新的 datetime 模块中添加了一组标准日期/时间类型。 有关更多详细信息,请参阅以下部分。
Distutils
Extension
类现在支持名为 depends 的额外构造函数参数,用于列出扩展所依赖的其他源文件。 如果任何依赖文件被修改,这让 Distutils 重新编译模块。 例如,如果sampmodule.c
包含头文件sample.h
,您将像这样创建Extension
对象:ext = Extension("samp", sources=["sampmodule.c"], depends=["sample.h"])
修改
sample.h
会导致模块被重新编译。 (由杰里米·希尔顿提供。)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'])
(彼得 Å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(lg 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]
(由凯文·奥康纳提供。)
IDLE 集成开发环境已使用 IDLEfork 项目 (http://idlefork.sourceforge.net) 中的代码进行更新。 最显着的特点是正在开发的代码现在在子进程中执行,这意味着不再需要手动
reload()
操作。 IDLE 的核心代码已作为idlelib
包合并到标准库中。imaplib 模块现在支持基于 SSL 的 IMAP。 (由皮尔斯·劳德和蒂诺·兰格提供。)
itertools 包含许多与迭代器一起使用的有用函数,其灵感来自 ML 和 Haskell 语言提供的各种函数。 例如,
itertools.ifilter(predicate, iterator)
返回迭代器中函数predicate()
返回 True 的所有元素,itertools.repeat(obj, N)
返回obj
]N 次。 该模块中还有许多其他功能; 有关详细信息,请参阅包的参考文档。 (雷蒙德·赫廷格供稿。)math 模块中的两个新函数,
degrees(rads)
和radians(degs)
,在弧度和度数之间进行转换。 math 模块中的其他函数,例如 math.sin() 和 math.cos() 总是需要以弧度为单位的输入值。 此外,一个可选的 base 参数被添加到 math.log() 以更容易计算除e
和10
以外的底数的对数. (雷蒙德·赫廷格供稿。)几个新的 POSIX 函数(
getpgid()
、killpg()
、lchown()
、loadavg()
、major()
、makedev()
、minor()
和mknod()
) 被添加到 os 模块基础的 posix 模块中。 (由 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 信号处理的支持被添加到 信号 中,但随后又被删除,因为事实证明它不可能跨平台可靠地工作。
socket 模块现在支持超时。 您可以在套接字对象上调用
settimeout(t)
方法来设置 t 秒的超时时间。 需要超过 t 秒才能完成的后续套接字操作将中止并引发 socket.timeout 异常。最初的超时实现是由 Tim O'Malley 实现的。 Michael Gilfix 将其集成到 Python socket 模块中,并对其进行了冗长的审查。 代码被签入后,Guido van Rossum 重写了其中的一部分。 (这是协作开发过程的一个很好的例子。)
在 Windows 上,socket 模块现在附带安全套接字层 (SSL) 支持。
C
PYTHON_API_VERSION
宏的值现在在 Python 级别公开为sys.api_version
。 当前异常可以通过调用新的 sys.exc_clear() 函数来清除。新的 tarfile 模块允许读取和写入 tar 格式的存档文件。 (拉尔斯·古斯塔贝尔供稿。)
新的 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()
函数都支持许多额外的关键字参数,用于微调格式; 有关详细信息,请参阅模块的文档。 (由格雷格·沃德提供。)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
类使用。 下面是一个简短的示例,该示例通过向其附加一个空的 Unicode 字符串或使用 unicode() 函数来确定将 8 位字符串转换为 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']
(雷蒙德·赫廷格供稿。)
xml.dom.minidom 中的 DOM 实现现在可以通过向 DOM 节点的
toxml()
和toprettyxml()
方法提供可选的编码参数来生成特定编码的 XML 输出。xmlrpclib 模块现在支持 XML-RPC 扩展,用于处理零数据值,例如 Python 的
None
。 解组 XML-RPC 响应时始终支持 Nil 值。 要生成包含None
的请求,您必须在创建Marshaller
实例时为 allow_none 参数提供真值。新的 DocXMLRPCServer 模块允许编写自文档化的 XML-RPC 服务器。 以演示模式(作为程序)运行它以查看它的运行情况。 将 Web 浏览器指向 RPC 服务器会生成 pydoc 样式的文档; 将 xmlrpclib 指向服务器允许调用实际方法。 (由布赖恩昆兰提供。)
添加了对国际化域名(RFC 3454、3490、3491 和 3492)的支持。 “idna”编码可用于在 Unicode 域名和该名称的 ASCII 兼容编码 (ACE) 之间进行转换。
>{}>{}> u"www.Alliancefrançaise.nu".encode("idna") 'www.xn--alliancefranaise-npb.nu'
socket 模块也被扩展为在将它们传递给 C 库之前透明地将 Unicode 主机名转换为 ACE 版本。 处理主机名的模块,如 httplib 和 ftplib) 也支持 Unicode 主机名; httplib 还使用域名的 ACE 版本发送 HTTP
Host
标头。 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)
,或使用多个类方法之一。 例如,date.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。
有关更多信息,请参阅模块的参考文档。 (由蒂姆·彼得斯提供。)
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
这将返回一个包含所有选项值的对象,以及一个包含剩余参数的字符串列表。
使用各种参数调用脚本现在可以像您期望的那样工作。 请注意,长度参数会自动转换为整数。
$ ./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() 和 [ X207X]PyObject_Free()。
- 要分配和释放 Python 对象,请使用“对象”系列 PyObject_New()、PyObject_NewVar() 和 PyObject_Del()。
感谢 Tim Peters 的大量工作,2.3 中的 pymalloc 还提供了调试功能来捕获扩展模块和解释器本身中的内存覆盖和双倍释放。 要启用此支持,请通过运行 configure 和 --with-pydebug
来编译 Python 解释器的调试版本。
为了帮助扩展编写者,头文件 Misc/pymemcompat.h
与 Python 2.3 的源代码一起分发,允许 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,
--with-cycle-gc
切换到 configure 已被删除。 - Python 现在可以通过在运行 Python 的 configure 脚本时提供
--enable-shared
来选择性地构建为共享库 (libpython2.3.so
)。 (由 Ondrej Palkovsky 提供。) DL_EXPORT
和DL_IMPORT
宏现已弃用。 Python 扩展模块的初始化函数现在应该使用新的宏PyMODINIT_FUNC
声明,而 Python 核心通常会使用PyAPI_FUNC
和PyAPI_DATA
宏。- 通过将
--without-doc-strings
提供给 configure 脚本,可以在没有任何内置函数和模块的文档字符串的情况下编译解释器。 这使得 Python 可执行文件大约小 10% s,但也意味着您无法获得 Python 内置程序的帮助。 (古斯塔沃·尼迈耶供稿。) PyArg_NoArgs()
宏现已弃用,应更改使用它的代码。 对于 Python 2.2 及更高版本,方法定义表可以指定 METH_NOARGS 标志,表示没有参数,然后可以删除参数检查。 如果与 Python 2.2 之前版本的兼容性很重要,代码可以使用PyArg_ParseTuple(args, "")
代替,但这会比使用 METH_NOARGS 慢。- PyArg_ParseTuple() 接受各种大小的无符号整数的新格式字符:
B
用于unsigned char
、H
用于unsigned short int
、[X] ] 表示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 参考文档或来源。
特定于端口的更改
对使用 EMX 运行时环境的 IBM OS/2 端口的支持已合并到主 Python 源代码树中。 EMX 是 OS/2 系统 API 上的 POSIX 仿真层。 EMX 的 Python 端口尝试支持 EMX 运行时公开的所有类似 POSIX 的功能,并且大部分都成功了; fork()
和 fcntl() 受到底层仿真层的限制。 作为将 EMX 端口集成到 CVS 的一部分,使用 IBM 的 Visual Age 编译器的标准 OS/2 端口也获得了对区分大小写的导入语义的支持。 (由安德鲁·麦金太尔提供。)
在 MacOS 上,大多数工具箱模块都已弱链接以提高向后兼容性。 这意味着如果当前操作系统版本中缺少单个例程,模块将不再无法加载。 相反,调用丢失的例程将引发异常。 (杰克·詹森供稿。)
在 Python 源代码分发的 Misc/RPM/
目录中找到的 RPM 规范文件已针对 2.3 进行了更新。 (由肖恩·赖夫施奈德提供。)
Python 现在支持的其他新平台包括 AtheOS (http://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
。除了在没有 -O 的情况下运行 Python 时稍微加快速度之外,很难检测到 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 现在总是一个关键字; 如果它在您的代码中用作变量名称,则必须选择不同的名称。
对于字符串 X 和 Y,如果 X 长度超过一个字符,
X in Y
现在有效。int() 类型构造函数现在将返回一个长整数,而不是在字符串或浮点数太大而无法放入整数时引发
OverflowError
。如果您有包含 8 位字符的 Unicode 字符串,则必须通过在文件顶部添加注释来声明文件的编码(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。