Python 2.4 的新特性 — Python 文档
Python 2.4 中的新功能
- 作者
- 是 库克林
本文介绍了 2005 年 3 月 30 日发布的 Python 2.4.1 中的新功能。
Python 2.4 是一个中等规模的版本。 它没有像激进的 Python 2.2 那样引入那么多的变化,但引入了比保守的 2.3 版本更多的特性。 最重要的新语言特性是函数装饰器和生成器表达式; 大多数其他更改都针对标准库。
根据 CVS 更改日志,在 Python 2.3 和 2.4 之间应用了 481 个补丁并修复了 502 个错误。 这两个数字都可能被低估。
本文并不试图提供每个新功能的完整规范,而是提供对每个功能的简要介绍。 有关完整的详细信息,您应该参考 Python 2.4 的文档,例如 Python 库参考和 Python 参考手册。 通常,您会被称为 PEP 以获得特定的新功能,以解释实现和设计原理。
PEP 218:内置集合对象
Python 2.3 引入了 sets
模块。 集合数据类型的 C 实现现在已作为两个新的内置类型添加到 Python 核心中,set(iterable)
和 frozenset(iterable)
。 它们为成员测试、从序列中消除重复项以及诸如并集、交集、差异和对称差异之类的数学运算提供高速运算。
>>> a = set('abracadabra') # form a set from a string
>>> 'z' in a # fast membership testing
False
>>> a # unique letters in a
set(['a', 'r', 'b', 'c', 'd'])
>>> ''.join(a) # convert back into a string
'arbcd'
>>> b = set('alacazam') # form a second set
>>> a - b # letters in a but not in b
set(['r', 'd', 'b'])
>>> a | b # letters in either a or b
set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
>>> a & b # letters in both a and b
set(['a', 'c'])
>>> a ^ b # letters in a or b but not both
set(['r', 'd', 'b', 'm', 'z', 'l'])
>>> a.add('z') # add a new element
>>> a.update('wxy') # add multiple new elements
>>> a
set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'x', 'z'])
>>> a.remove('x') # take one element out
>>> a
set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'z'])
frozenset() 类型是 set() 的不可变版本。 由于它是不可变和可散列的,因此它可以用作字典键或另一个集合的成员。
sets
模块保留在标准库中,如果您希望对 Set
或 ImmutableSet
类进行子类化,它可能很有用。 目前没有计划弃用该模块。
PEP 237:统一长整数和整数
此 PEP 的漫长过渡过程始于 Python 2.2,在 Python 2.4 中又向前迈进了一步。 在 2.3 中,某些整数运算在 int/long 统一触发 FutureWarning 警告并返回限制为 32 或 64 位的值(取决于您的平台)后表现不同。 在 2.4 中,这些表达式不再产生警告,而是产生不同的结果,通常是一个长整数。
有问题的表达式主要是左移和冗长的十六进制和八进制常量。 例如,2 << 32
在 2.3 中导致警告,在 32 位平台上计算为 0。 在 Python 2.4 中,此表达式现在返回正确答案 8589934592。
PEP 289:生成器表达式
Python 2.2 中引入的迭代器功能和 itertools 模块可以更轻松地编写循环遍历大型数据集的程序,而无需一次将整个数据集保存在内存中。 列表推导式不太适合这张图,因为它们生成了一个包含所有项目的 Python 列表对象。 这不可避免地将所有对象拉入内存,如果您的数据集非常大,这可能是一个问题。 当试图编写一个函数式风格的程序时,很自然地编写如下内容:
links = [link for link in get_all_links() if not link.followed]
for link in links:
...
代替
for link in get_all_links():
if link.followed:
continue
...
第一种形式更简洁,可能更具可读性,但如果您正在处理大量链接对象,则必须编写第二种形式以避免将所有链接对象同时放入内存中。
生成器表达式的工作方式类似于列表推导式,但不会具体化整个列表; 相反,他们创建了一个生成器,它将一个一个地返回元素。 上面的例子可以写成:
links = (link for link in get_all_links() if not link.followed)
for link in links:
...
生成器表达式总是必须写在括号内,如上例所示。 表示函数调用的括号也很重要,因此如果您想创建一个将立即传递给函数的迭代器,您可以编写:
print sum(obj.count for obj in list_all_objects())
生成器表达式与列表推导式在各种小方面有所不同。 最值得注意的是,循环变量(上面例子中的 obj)在生成器表达式之外是不可访问的。 列表推导式将变量分配给它的最后一个值; Python 的未来版本将改变这一点,使列表推导式在这方面与生成器表达式相匹配。
PEP 292:更简单的字符串替换
标准库中的一些新类提供了将变量替换为字符串的替代机制; 这种替换风格可能更适合未经培训的用户需要编辑模板的应用程序。
按名称替换变量的常用方法是 %
运算符:
>>> '%(page)i: %(title)s' % {'page':2, 'title': 'The Best of Times'}
'2: The Best of Times'
在编写模板字符串时,很容易忘记右括号后的 i
或 s
。 如果模板在 Python 模块中,这不是大问题,因为您运行代码,得到“不支持的格式字符”ValueError,然后修复问题。 但是,考虑一个应用程序,例如 Mailman,其中模板字符串或翻译由不了解 Python 语言的用户编辑。 向这些用户解释格式字符串的语法很复杂,如果他们犯了错误,也很难向他们提供有用的反馈。
PEP 292 将 Template
类添加到 string 模块中,该模块使用 $
来指示替换:
>>> import string
>>> t = string.Template('$page: $title')
>>> t.substitute({'page':2, 'title': 'The Best of Times'})
'2: The Best of Times'
如果字典中缺少键,substitute()
方法将引发 KeyError。 还有一个 safe_substitute()
方法可以忽略丢失的键:
>>> t = string.Template('$page: $title')
>>> t.safe_substitute({'page':3})
'3: $title'
PEP 318:函数和方法的装饰器
Python 2.2 通过添加静态方法和类方法扩展了 Python 的对象模型,但它没有扩展 Python 的语法以提供任何定义静态或类方法的新方法。 相反,您必须以通常的方式编写 def 语句,并将结果方法传递给 staticmethod() 或 classmethod() 函数,该函数将包装将函数作为新类型的方法。 您的代码如下所示:
class C:
def meth (cls):
...
meth = classmethod(meth) # Rebind name to wrapped-up class method
如果方法很长,很容易错过或忘记函数体后面的 classmethod() 调用。
其意图始终是添加一些语法以使此类定义更具可读性,但在 2.2 发布时,好的语法并不明显。 今天,一个好的语法 still 并不明显,但用户要求更容易访问该功能; 添加了一个新的语法特性来满足这一需求。
新功能称为“函数装饰器”。 这个名字来源于classmethod()、staticmethod()和朋友们在一个函数对象上存储附加信息的想法; 它们是 装饰 功能更多细节。
该符号借用 Java 并使用 '@'
字符作为指示符。 使用新语法,上面的例子将被写成:
class C:
@classmethod
def meth (cls):
...
@classmethod
是 meth=classmethod(meth)
赋值的简写。 更一般地说,如果您有以下情况:
@A
@B
@C
def f ():
...
它相当于下面的预装饰器代码:
def f(): ...
f = A(B(C(f)))
装饰器必须在函数定义之前,每行一个装饰器,并且不能与 def 语句在同一行,这意味着 @A def f(): ...
是非法的。 您只能在模块级别或类内部装饰函数定义; 你不能装饰类定义。
装饰器只是一个函数,它将要装饰的函数作为参数并返回相同的函数或某个新对象。 装饰器的返回值不需要是可调用的(尽管它通常是),除非将进一步的装饰器应用于结果。 编写自己的装饰器很容易。 下面的简单示例只是在函数对象上设置一个属性:
>>> def deco(func):
... func.attr = 'decorated'
... return func
...
>>> @deco
... def f(): pass
...
>>> f
<function f at 0x402ef0d4>
>>> f.attr
'decorated'
>>>
作为一个更现实的例子,以下装饰器检查提供的参数是否为整数:
def require_int (func):
def wrapper (arg):
assert isinstance(arg, int)
return func(arg)
return wrapper
@require_int
def p1 (arg):
print arg
@require_int
def p2(arg):
print arg*2
PEP 318 中的一个例子包含了这个想法的一个更高级的版本,它允许你指定所需的类型并检查返回的类型。
装饰器函数可以带参数。 如果提供了参数,则仅使用这些参数调用装饰器函数,并且必须返回新的装饰器函数; 如前所述,此函数必须采用单个函数并返回一个函数。 换句话说,@A @B @C(args)
变成:
def f(): ...
_deco = C(args)
f = A(B(_deco(f)))
正确地做到这一点可能有点费脑筋,但这并不太难。
一个小的相关更改使函数的 func_name
属性可写。 此属性用于在回溯中显示函数名称,因此装饰器应更改构造和返回的任何新函数的名称。
也可以看看
- PEP 318 - 函数、方法和类的装饰器
- 由凯文 D 撰写 史密斯、吉姆·朱伊特和跳过蒙塔纳罗。 有几个人编写了实现函数装饰器的补丁,但实际签入的是由 Mark Russell 编写的补丁 #979728。
- https://wiki.python.org/moin/PythonDecoratorLibrary
- 此 Wiki 页面包含几个装饰器示例。
PEP 322:反向迭代
一个新的内置函数 reversed(seq)
接受一个序列并返回一个迭代器,该迭代器以相反的顺序循环遍历该序列的元素。
>>> for i in reversed(xrange(1,4)):
... print i
...
3
2
1
与扩展切片(例如 range(1,4)[::-1]
)相比,reversed() 更易于阅读,运行速度更快,并且使用的内存要少得多。
请注意, reversed() 只接受序列,而不是任意迭代器。 如果要反转迭代器,请首先使用 list() 将其转换为列表。
>>> input = open('/etc/passwd', 'r')
>>> for line in reversed(list(input)):
... print line
...
root:*:0:0:System Administrator:/var/root:/bin/tcsh
...
PEP 324:新的子流程模块
标准库提供了多种执行子流程的方法,提供不同的功能和不同级别的复杂性。 os.system(command)
易于使用,但速度慢(它运行执行命令的 shell 进程)且危险(您必须小心转义 shell 的元字符)。 popen2
模块提供了可以从子进程捕获标准输出和标准错误的类,但命名令人困惑。 subprocess 模块对此进行了清理,提供了一个统一的界面,可以提供您可能需要的所有功能。
代替 popen2
的类集合,subprocess 包含一个名为 Popen
的类,其构造函数支持许多不同的关键字参数。
class Popen(args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=False, shell=False,
cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0):
args 通常是一个字符串序列,它们将作为作为子进程执行的程序的参数。 (如果 shell 参数为真,args 可以是一个字符串,然后将传递给 shell 进行解释,就像 os.system()做。)
stdin、stdout 和 stderr 指定子进程的输入、输出和错误流将是什么。 您可以提供文件对象或文件描述符,也可以使用常量 subprocess.PIPE
在子进程和父进程之间创建管道。
构造函数有许多方便的选项:
- close_fds 请求在运行子进程之前关闭所有文件描述符。
- cwd 指定子进程将在其中执行的工作目录(默认为父进程的任何工作目录)。
- env 是一个指定环境变量的字典。
- preexec_fn 是在子进程启动之前调用的函数。
- universal_newlines 使用 Python 的 universal newlines 功能打开孩子的输入和输出。
一旦你创建了 Popen
实例,你可以调用它的 wait()
方法暂停直到子进程退出,poll()
检查它是否在没有暂停的情况下退出,或者 communicate(data)
将字符串 data 发送到子进程的标准输入。 communicate(data)
然后读取子进程发送到其标准输出或标准错误的任何数据,返回一个元组 (stdout_data, stderr_data)
。
call()
是一个快捷方式,将其参数传递给 Popen
构造函数,等待命令完成,并返回子进程的状态代码。 它可以作为 os.system() 的更安全的模拟:
sts = subprocess.call(['dpkg', '-i', '/tmp/new-package.deb'])
if sts == 0:
# Success
...
else:
# dpkg returned an error
...
该命令是在不使用 shell 的情况下调用的。 如果你真的想使用 shell,你可以添加 shell=True
作为关键字参数并提供一个字符串而不是一个序列:
sts = subprocess.call('dpkg -i /tmp/new-package.deb', shell=True)
PEP 采用各种 shell 和 Python 代码示例,并展示了如何将它们转换为使用 子进程 的 Python 代码。 强烈建议阅读 PEP 的这一部分。
PEP 327:十进制数据类型
Python 一直支持基于底层 C double 类型的浮点 (FP) 数作为数据类型。 然而,虽然大多数编程语言都提供浮点类型,但许多人(甚至程序员)并不知道浮点数不能准确地表示某些小数。 新的 Decimal
类型可以准确地表示这些分数,最高可达用户指定的精度限制。
为什么需要十进制?
限制来自用于浮点数的表示。 FP 数字由三个部分组成:
- 正负号。
- 尾数,它是一位二进制数,后跟小数部分。 例如,以 2 进制表示的
1.01
是1 + 0/2 + 1/4
,或十进制表示法中的 1.25。 - 指数,它告诉小数点在所表示的数字中的位置。
例如,数字 1.25 为正号,尾数值为 1.01(二进制),指数为 0(小数点不需要移动)。 数字 5 具有相同的符号和尾数,但指数为 2,因为尾数乘以 4(2 的指数 2 次方); 1.25 * 4 等于 5。
现代系统通常提供符合 IEEE 754 标准的浮点支持。 C 的 double 类型通常实现为 64 位 IEEE 754 数字,它使用 52 位空间作为尾数。 这意味着数字只能指定为 52 位精度。 如果您试图表示无限重复扩展的数字,则扩展会在 52 位后被切断。 不幸的是,大多数软件需要产生以 10 为基数的输出,而以 10 为基数的常见分数通常是重复二进制的十进制数。 例如1.1十进制就是二进制1.0001100110011 ...
; .1 = 1/16 + 1/32 + 1/256 加上无限数量的附加项。 IEEE 754 必须在 52 位数字后去掉无限重复的小数,因此表示有点不准确。
有时您会在打印数字时看到这种不准确:
>>> 1.1
1.1000000000000001
当您打印数字时,不准确并不总是可见的,因为 FP 到十进制字符串的转换是由 C 库提供的,并且大多数 C 库都试图产生合理的输出。 然而,即使它不显示,不准确仍然存在,后续操作可能会放大错误。
对于许多应用程序,这无关紧要。 如果我正在绘制点并将它们显示在我的监视器上,1.1 和 1.10000000000000001 之间的差异太小而无法看到。 报告通常将输出限制为一定数量的小数位,如果将数字四舍五入到两位、三位甚至八位小数,则错误永远不会明显。 但是,对于重要的应用程序,实现您自己的自定义算术例程需要大量工作。
因此,创建了 Decimal
类型。
Decimal型
Python 的标准库中添加了一个新模块 decimal。 它包含两个类,Decimal
和 Context
。 Decimal
实例代表数字,Context
实例用于包装精度和默认舍入模式等各种设置。
Decimal
实例是不可变的,就像常规的 Python 整数和 FP 数一样; 一旦它被创建,你就不能改变一个实例代表的值。 Decimal
实例可以从整数或字符串创建:
>>> import decimal
>>> decimal.Decimal(1972)
Decimal("1972")
>>> decimal.Decimal("1.1")
Decimal("1.1")
您还可以提供包含符号、表示为十进制数字元组的尾数和指数的元组:
>>> decimal.Decimal((1, (1, 4, 7, 5), -2))
Decimal("-14.75")
注意事项:符号位是布尔值,因此 0 为正,1 为负。
从浮点数转换会带来一些问题:代表 1.1 的 FP 数是否应该变成十进制数正好是 1.1,还是 1.1 加上引入的任何不准确之处? 决定是为了避免这个问题并将这样的转换排除在 API 之外。 相反,您应该使用所需的精度将浮点数转换为字符串,并将该字符串传递给 Decimal
构造函数:
>>> f = 1.1
>>> decimal.Decimal(str(f))
Decimal("1.1")
>>> decimal.Decimal('%.12f' % f)
Decimal("1.100000000000")
一旦您拥有 Decimal
实例,您就可以对它们执行通常的数学运算。 一个限制:求幂需要一个整数指数:
>>> a = decimal.Decimal('35.72')
>>> b = decimal.Decimal('1.73')
>>> a+b
Decimal("37.45")
>>> a-b
Decimal("33.99")
>>> a*b
Decimal("61.7956")
>>> a/b
Decimal("20.64739884393063583815028902")
>>> a ** 2
Decimal("1275.9184")
>>> a**b
Traceback (most recent call last):
...
decimal.InvalidOperation: x ** (non-integer)
您可以将 Decimal
实例与整数组合,但不能与浮点数组合:
>>> a + 4
Decimal("39.72")
>>> a + 4.5
Traceback (most recent call last):
...
TypeError: You can interact Decimal only with int, long or Decimal data types.
>>>
Decimal
数可以与 math 和 cmath 模块一起使用,但请注意,在执行运算之前,它们会立即转换为浮点数,结果可能会损失精度和准确度。 您还将得到一个常规的浮点数,而不是 Decimal
。
>>> import math, cmath
>>> d = decimal.Decimal('123456789012.345')
>>> math.sqrt(d)
351364.18288201344
>>> cmath.sqrt(-d)
351364.18288201344j
Decimal
实例有一个 sqrt()
方法,该方法返回一个 Decimal
,但是如果您需要其他东西,例如三角函数,则必须实现它们。
>>> d.sqrt()
Decimal("351364.1828820134592177245001")
Context型
Context
类的实例封装了十进制运算的几个设置:
prec
为精度,小数位数。rounding
指定舍入模式。 decimal 模块具有用于各种可能性的常量:ROUND_DOWN
、ROUND_CEILING
、ROUND_HALF_EVEN
和各种其他。traps
是一个字典,指定在遇到某些错误条件时会发生什么:引发异常或返回值。 错误条件的一些示例是被零除、精度损失和溢出。
通过调用 getcontext()
可以使用线程本地默认上下文; 您可以更改此上下文的属性以更改默认精度、舍入或陷阱处理。 以下示例显示了更改默认上下文精度的效果:
>>> decimal.getcontext().prec
28
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal("0.1428571428571428571428571429")
>>> decimal.getcontext().prec = 9
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal("0.142857143")
错误条件的默认操作是可选的; 该模块可以返回一个特殊值,例如无穷大或非数字,或者可以引发异常:
>>> decimal.Decimal(1) / decimal.Decimal(0)
Traceback (most recent call last):
...
decimal.DivisionByZero: x / 0
>>> decimal.getcontext().traps[decimal.DivisionByZero] = False
>>> decimal.Decimal(1) / decimal.Decimal(0)
Decimal("Infinity")
>>>
Context
实例也有多种格式化数字的方法,例如 to_eng_string()
和 to_sci_string()
。
有关详细信息,请参阅 decimal 模块的文档,其中包括快速入门教程和参考。
也可以看看
- PEP 327 - 十进制数据类型
- 由 Facundo Batista 编写,由 Facundo Batista、Eric Price、Raymond Hettinger、Aahz 和 Tim Peters 实施。
- http://www.lahey.com/float.htm
- 本文使用 Fortran 代码来说明浮点不准确可能导致的许多问题。
- http://speleotrove.com/decimal/
- 基于十进制表示的描述。 这种表示被提议作为标准,并且是新的 Python 十进制类型的基础。 这些材料的大部分是由 Rexx 语言的设计者 Mike Cowlishaw 编写的。
PEP 328:多行导入
一种语言更改是一个小的语法调整,旨在使从模块中导入许多名称变得更加容易。 在 from module import names
语句中,names 是由逗号分隔的名称序列。 如果序列很长,您可以从同一个模块中编写多个导入,或者您可以使用反斜杠来转义行结尾,如下所示:
from SimpleXMLRPCServer import SimpleXMLRPCServer,\
SimpleXMLRPCRequestHandler,\
CGIXMLRPCRequestHandler,\
resolve_dotted_attribute
Python 2.4 中的语法更改只是允许将名称放在括号内。 Python 忽略括号表达式中的换行符,因此不再需要反斜杠:
from SimpleXMLRPCServer import (SimpleXMLRPCServer,
SimpleXMLRPCRequestHandler,
CGIXMLRPCRequestHandler,
resolve_dotted_attribute)
PEP 还建议所有 import 语句都是绝对导入,前导 .
字符表示相对导入。 PEP 的这部分不是为 Python 2.4 实现的,而是为 Python 2.5 完成的。
PEP 331:与语言环境无关的浮点/字符串转换
locale 模块允许 Python 软件选择本地化为特定国家或语言的各种转换和显示约定。 但是,该模块小心地不更改数字语言环境,因为 Python 实现中的各种函数要求数字语言环境保持设置为 'C'
语言环境。 这通常是因为代码使用了 C 库的 atof()
函数。
但是,不设置数字语言环境会给使用第三方 C 库的扩展带来麻烦,因为它们没有正确的语言环境设置。 激励示例是 GTK+,其用户界面小部件未在当前语言环境中显示数字。
PEP 中描述的解决方案是向 Python API 添加三个新函数,这些函数执行 ASCII-only 转换,忽略区域设置:
PyOS_ascii_strtod(str, ptr)
和PyOS_ascii_atof(str, ptr)
都将字符串转换为 C double。PyOS_ascii_formatd(buffer, buf_len, format, d)
将 double 转换为 ASCII 字符串。
这些函数的代码来自 GLib 库(https://developer.gnome.org/glib/stable/),其开发者好心地将相关函数重新授权并捐赠给了 Python 软件基金会。 locale 模块现在可以更改数字区域设置,让 GTK+ 等扩展产生正确的结果。
其他语言更改
以下是 Python 2.4 对核心 Python 语言所做的所有更改。
添加了函数和方法的装饰器(PEP 318)。
添加了内置 set() 和 frozenset() 类型(PEP 218)。 其他新的内置函数包括
reversed(seq)
函数(PEP 322)。添加了生成器表达式(PEP 289)。
某些数值表达式不再返回限制为 32 或 64 位的值(PEP 237)。
您现在可以在
from module import names
语句 (PEP 328) 中的名称列表周围加上括号。dict.update() 方法现在接受与 dict 构造函数相同的参数形式。 这包括任何映射、任何可迭代的键/值对和关键字参数。 (雷蒙德·赫廷格供稿。)
字符串方法
ljust()
、rjust()
和center()
现在采用可选参数来指定空格以外的填充字符。 (雷蒙德·赫廷格供稿。)字符串还获得了
rsplit()
方法,其工作方式类似于split()
方法,但从字符串的末尾拆分。 (由肖恩·赖夫施奈德提供。)>>> 'www.python.org'.split('.', 1) ['www', 'python.org'] 'www.python.org'.rsplit('.', 1) ['www.python', 'org']
三个关键字参数,cmp、key和reverse,被添加到列表的
sort()
方法中。 这些参数使sort()
的一些常见用法变得更简单。 所有这些参数都是可选的。对于 cmp 参数,该值应该是一个比较函数,它接受两个参数并根据参数的比较方式返回 -1、0 或 +1。 然后将使用此函数对列表进行排序。 以前,这是唯一可以提供给
sort()
的参数。key 应该是一个单参数函数,它接受一个列表元素并返回该元素的比较键。 然后使用比较键对列表进行排序。 以下示例不区分大小写地对列表进行排序:
>>> L = ['A', 'b', 'c', 'D'] >>> L.sort() # Case-sensitive sort >>> L ['A', 'D', 'b', 'c'] >>> # Using 'key' parameter to sort list >>> L.sort(key=lambda x: x.lower()) >>> L ['A', 'b', 'c', 'D'] >>> # Old-fashioned way >>> L.sort(cmp=lambda x,y: cmp(x.lower(), y.lower())) >>> L ['A', 'b', 'c', 'D']
最后一个示例使用 cmp 参数,是执行不区分大小写排序的旧方法。 它可以工作,但比使用 key 参数慢。 使用 key 为列表中的每个元素调用一次
lower()
方法,而使用 cmp 将在每次比较时调用它两次,因此使用 key 节省在调用lower()
方法时。对于简单的键函数和比较函数,通常可以通过使用未绑定的方法来避免 lambda 表达式。 例如,上面不区分大小写的排序最好写成:
>>> L.sort(key=str.lower) >>> L ['A', 'b', 'c', 'D']
最后,reverse 参数采用布尔值。 如果值为 true,则列表将按相反顺序排序。 您现在可以编写
L.sort(reverse=True)
,而不是L.sort(); L.reverse()
。现在保证排序的结果是稳定的。 这意味着具有相同键的两个条目将按照与输入相同的顺序返回。 例如,您可以按姓名对人员列表进行排序,然后按年龄对列表进行排序,从而得到按年龄排序的列表,其中年龄相同的人员按姓名排序。
(对
sort()
的所有更改均由 Raymond Hettinger 提供。)有一个新的内置函数
sorted(iterable)
,其工作方式类似于就地 list.sort() 方法,但可以在表达式中使用。 区别在于:输入可以是任何可迭代的;
对新形成的副本进行排序,原件完好无损; 和
表达式返回新的排序副本
>>> L = [9,7,8,3,2,4,1,6,5] >>> [10+i for i in sorted(L)] # usable in a list comprehension [11, 12, 13, 14, 15, 16, 17, 18, 19] >>> L # original is left unchanged [9,7,8,3,2,4,1,6,5] >>> sorted('Monty Python') # any iterable may be an input [' ', 'M', 'P', 'h', 'n', 'n', 'o', 'o', 't', 't', 'y', 'y'] >>> # List the contents of a dict sorted by key values >>> colormap = dict(red=1, blue=2, green=3, black=4, yellow=5) >>> for k, v in sorted(colormap.iteritems()): ... print k, v ... black 4 blue 2 green 3 red 1 yellow 5
(雷蒙德·赫廷格供稿。)
整数运算将不再触发
OverflowWarning
。OverflowWarning
警告将在 Python 2.5 中消失。解释器获得了一个新的开关,-m,它接受一个名称,在
sys.path
上搜索相应的模块,并将该模块作为脚本运行。 例如,您现在可以使用python -m profile
运行 Python 分析器。 (由尼克·科格兰提供。)eval(expr, globals, locals)
和execfile(filename, globals, locals)
函数以及exec
语句现在接受 locals 参数的任何映射类型。 以前这必须是一个普通的 Python 字典。 (雷蒙德·赫廷格供稿。)zip() 内置函数和
itertools.izip()
现在在不带参数调用时返回一个空列表。 以前他们提出了 TypeError 异常。 这使它们更适合与可变长度参数列表一起使用:>>> def transpose(array): ... return zip(*array) ... >>> transpose([(1,2,3), (4,5,6)]) [(1, 4), (2, 5), (3, 6)] >>> transpose([]) []
(雷蒙德·赫廷格供稿。)
导入模块时遇到失败不再在
sys.modules
中留下部分初始化的模块对象。 留下的不完整模块对象会欺骗相同模块的进一步导入,从而导致混淆错误。 (由蒂姆·彼得斯修正。)None 现在是一个常数; 将新值绑定到名称
None
的代码现在是一个语法错误。 (雷蒙德·赫廷格供稿。)
优化
- 列表和元组切片的内部循环得到了优化,现在运行速度提高了大约三分之一。 字典的内部循环也得到了优化,从而提高了
keys()
、values()
、items()
、iterkeys()
、itervalues()
和iteritems()
。 (雷蒙德·赫廷格供稿。) - 增长和缩小列表的机制针对速度和空间效率进行了优化。 由于更高效的代码路径和更少使用底层系统
realloc()
,从列表中追加和弹出现在运行得更快。 列表理解也有好处。list.extend()
也进行了优化,在扩展基本列表之前不再将其参数转换为临时列表。 (雷蒙德·赫廷格供稿。) - list()、tuple()、map()、filter()和zip()现在使用提供
__len__()
方法的非序列参数运行速度提高数倍。 (雷蒙德·赫廷格供稿。) - 方法
list.__getitem__()
、dict.__getitem__()
和dict.__contains__()
现在被实现为method_descriptor
对象而不是wrapper_descriptor
对象。 这种访问形式使它们的性能翻倍,并使它们更适合用作函数的参数:map(mydict.__getitem__, keylist)
。 (雷蒙德·赫廷格供稿。) - 添加了一个新的操作码
LIST_APPEND
,它简化了列表推导式生成的字节码,并将它们的速度提高了大约三分之一。 (雷蒙德·赫廷格供稿。) - 窥视孔字节码优化器已得到改进,以生成更短、更快的字节码; 值得注意的是,生成的字节码更具可读性。 (由 Raymond Hettinger 增强。)
- 在某些情况下,现在可以更有效地执行
s = s + "abc"
和s += "abc"
形式的语句中的字符串连接。 这种优化不会出现在其他 Python 实现(例如 Jython)中,因此您不应依赖它; 当您想有效地将大量字符串粘合在一起时,仍然建议使用字符串的join()
方法。 (由 Armin Rigo 提供。)
2.4 优化的最终结果是 Python 2.4 运行的 pystone 基准测试比 Python 2.3 高 5% faster,比 Python 2.2 高 35% faster。 (pystone 不是一个特别好的基准,但它是 Python 性能最常用的衡量标准。 您自己的应用程序可能会从 Python 2.4 中显示出或多或少的好处。)
新的、改进的和弃用的模块
像往常一样,Python 的标准库得到了许多增强和错误修复。 这是最显着更改的部分列表,按模块名称的字母顺序排序。 请查阅源代码树中的 Misc/NEWS
文件以获取更完整的更改列表,或查看 CVS 日志以获取所有详细信息。
asyncore 模块的
loop()
函数现在具有 count 参数,可让您通过轮询循环执行有限次数的传递。 默认仍然是永远循环。base64 模块现在有更完整的 RFC 3548 支持 Base64、Base32 和 Base16 编码和解码,包括可选的大小写折叠和可选的替代字母表。 (由巴里华沙提供。)
bisect 模块现在具有底层 C 实现以提高性能。 (德米特里·瓦西里耶夫供稿。)
由 Hye-Shik Chang 维护的东亚编解码器的 CJKCodecs 集合已集成到 2.4 中。 新的编码是:
中文(中国):gb2312、gbk、gb18030、big5hkscs、hz
中文 (ROC): big5, cp950
- 日语:cp932、euc-jis-2004、euc-jp、euc-jisx0213、iso-2022-jp、
iso-2022-jp-1、iso-2022-jp-2、iso-2022-jp-3、iso-2022-jp-ext、iso-2022-jp-2004、shift-jis、shift-jisx0213、shift- jis-2004
韩语:cp949、euc-kr、johab、iso-2022-kr
添加了其他一些新编码:HP Roman8、ISO_8859-11、ISO_8859-16、PCTP-154 和 TIS-620。
UTF-8 和 UTF-16 编解码器现在可以更好地接收部分输入。 以前,
StreamReader
类会尝试读取更多数据,从而无法从流中恢复解码。read()
方法现在将返回尽可能多的数据,并且未来的调用将在前一个中断的地方继续解码。 (由 Walter Dörwald 实施。)有一个新的 collections 模块用于各种专门的集合数据类型。 目前它只包含一种类型,
deque
,一个双端队列,支持从任一端有效地添加和删除元素:>>> from collections import deque >>> d = deque('ghi') # make a new deque with three items >>> d.append('j') # add a new entry to the right side >>> d.appendleft('f') # add a new entry to the left side >>> d # show the representation of the deque deque(['f', 'g', 'h', 'i', 'j']) >>> d.pop() # return and remove the rightmost item 'j' >>> d.popleft() # return and remove the leftmost item 'f' >>> list(d) # list the contents of the deque ['g', 'h', 'i'] >>> 'h' in d # search the deque True
几个模块,例如
Queue
和 threading 模块,现在利用 collections.deque 来提高性能。 (雷蒙德·赫廷格供稿。)ConfigParser
类已略有增强。read()
方法现在返回已成功解析的文件列表,如果传递 值 ,set() 方法会引发 TypeError不是字符串的参数。 (由约翰贝尔蒙特和大卫古德提供。)curses 模块现在支持 ncurses 扩展
use_default_colors()
。 在终端支持透明的平台上,这使得使用透明背景成为可能。 (约尔格·莱曼供稿。)difflib 模块现在包含一个
HtmlDiff
类,该类创建一个 HTML 表格,显示文本的两个版本的并排比较。 (丹·加斯供稿。)email 包已更新到 3.0 版,它删除了各种已弃用的 API,并取消了对 2.3 之前的 Python 版本的支持。 该包的 3.0 版本使用新的 MIME 消息增量解析器,可在
email.FeedParser
模块中使用。 新的解析器不需要将整个消息读入内存,并且如果消息格式错误也不会引发异常; 相反,它会在消息的defect
属性中记录任何问题。 (由 Anthony Baxter、Barry Warsaw、Thomas Wouters 等开发。)heapq 模块已转换为 C。 由此带来的十倍速度提升使该模块适用于处理大量数据。 此外,该模块还有两个新函数
nlargest()
和nsmallest()
,它们使用堆来查找数据集中的 N 个最大值或最小值,而无需进行完整排序。 (雷蒙德·赫廷格供稿。)httplib
模块现在包含在各种与 HTTP 相关的 RFC 文档中定义的 HTTP 状态代码的常量。 常量的名称有OK
、CREATED
、CONTINUE
和MOVED_PERMANENTLY
; 使用 pydoc 获取完整列表。 (由安德鲁·埃兰提供。)imaplib 模块现在支持 IMAP 的 THREAD 命令(由 Yves Dionne 提供)和新的
deleteacl()
和myrights()
方法(由 Arnaud Mazin 提供)。itertools 模块获得了
groupby(iterable[, *func*])
功能。 iterable 是可以迭代返回元素流的东西,可选的 func 参数是一个函数,它接受一个元素并返回一个键值; 如果省略,键就是元素本身。groupby()
然后将元素分组为具有匹配键值的子序列,并返回一系列包含键值和子序列迭代器的 2 元组。这是一个更清楚的例子。 key 函数只返回一个数字是偶数还是奇数,所以
groupby()
的结果是返回连续运行的奇数或偶数。>>> import itertools >>> L = [2, 4, 6, 7, 8, 9, 11, 12, 14] >>> for key_val, it in itertools.groupby(L, lambda x: x % 2): ... print key_val, list(it) ... 0 [2, 4, 6] 1 [7] 0 [8] 1 [9, 11] 0 [12, 14] >>>
groupby()
通常与排序输入一起使用。groupby()
的逻辑类似于 Unixuniq
过滤器,这使得它可以方便地消除、计数或识别重复元素:>>> word = 'abracadabra' >>> letters = sorted(word) # Turn string into a sorted list of letters >>> letters ['a', 'a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'r', 'r'] >>> for k, g in itertools.groupby(letters): ... print k, list(g) ... a ['a', 'a', 'a', 'a', 'a'] b ['b', 'b'] c ['c'] d ['d'] r ['r', 'r'] >>> # List unique letters >>> [k for k, g in groupby(letters)] ['a', 'b', 'c', 'd', 'r'] >>> # Count letter occurrences >>> [(k, len(list(g))) for k, g in groupby(letters)] [('a', 5), ('b', 2), ('c', 1), ('d', 1), ('r', 2)]
(由 Hye-Shik Chang 提供。)
itertools 还获得了一个名为
tee(iterator, N)
的函数,该函数返回复制 iterator 的 N 独立迭代器。 如果省略 N,则默认为 2。>>> L = [1,2,3] >>> i1, i2 = itertools.tee(L) >>> i1,i2 (<itertools.tee object at 0x402c2080>, <itertools.tee object at 0x402c2090>) >>> list(i1) # Run the first iterator to exhaustion [1, 2, 3] >>> list(i2) # Run the second iterator to exhaustion [1, 2, 3]
请注意,
tee()
必须保留迭代器返回值的副本; 在最坏的情况下,它可能需要保留所有这些。 因此,如果前导迭代器可以在长输入流中远远领先于尾随迭代器,则应谨慎使用此方法。 如果间隔很大,那么您不妨使用 list() 代替。 当迭代器彼此紧密跟踪时,tee()
是理想的。 可能的应用包括书签、窗口化或前瞻迭代器。 (雷蒙德·赫廷格供稿。)许多函数被添加到 locale 模块,例如
bind_textdomain_codeset()
指定特定编码和一系列l*gettext()
函数,以所选编码返回消息。 (古斯塔沃·尼迈耶供稿。)logging 包的
basicConfig()
函数中添加了一些关键字参数以简化日志配置。 默认行为是将消息记录到标准错误,但可以指定各种关键字参数以记录到特定文件、更改日志记录格式或设置日志记录级别。 例如:import logging logging.basicConfig(filename='/var/log/application.log', level=0, # Log all messages format='%(levelname):%(process):%(thread):%(message)')
logging 包的其他新增功能包括
log(level, msg)
便利方法,以及按时间间隔轮换其日志文件的TimedRotatingFileHandler
类。 该模块已经有RotatingFileHandler
,一旦文件超过一定大小,它就会轮换日志。 这两个类都源自一个新的BaseRotatingHandler
类,该类可用于实现其他旋转处理程序。(由 Vinay Sajip 实施的更改。)
marshal 模块现在在解包数据结构时共享内部字符串。 这可能会缩小某些 pickle 字符串的大小,但主要效果是使
.pyc
文件明显更小。 (由 Martin von Löwis 提供。)nntplib 模块的
NNTP
类获得了description()
和descriptions()
方法来检索单个组或一系列组的新闻组描述。 (由 Jürgen A. 艾哈德。)operator模块增加了两个新功能,
attrgetter(attr)
和itemgetter(index)
。 这两个函数都返回带有单个参数并返回相应属性或项目的可调用对象; 当与 map() 或 sorted() 一起使用时,这些可调用函数成为出色的数据提取器。 例如:>>> L = [('c', 2), ('d', 1), ('a', 4), ('b', 3)] >>> map(operator.itemgetter(0), L) ['c', 'd', 'a', 'b'] >>> map(operator.itemgetter(1), L) [2, 1, 4, 3] >>> sorted(L, key=operator.itemgetter(1)) # Sort list by second tuple item [('d', 1), ('c', 2), ('b', 3), ('a', 4)]
(雷蒙德·赫廷格供稿。)
optparse 模块以各种方式更新。 该模块现在通过 gettext.gettext() 传递其消息,从而可以国际化 Optik 的帮助和错误消息。 选项的帮助消息现在可以包含字符串
'%default'
,它将被选项的默认值替换。 (由格雷格·沃德提供。)长期计划是在未来的 Python 版本中弃用
rfc822
模块,以支持 email 包。 为此,email.Utils.formatdate()
功能已更改,使其可用作rfc822.formatdate()
的替代品。 考虑到这一点,您可能希望编写新的电子邮件处理代码。 (由 Anthony Baxter 实施的更改。)一个新的
urandom(n)
函数被添加到 os 模块,返回一个包含 n 字节随机数据的字符串。 此函数提供对特定于平台的随机源的访问,例如 Linux 上的/dev/urandom
或 Windows CryptoAPI。 (由特雷弗·佩林提供。)另一个新函数:如果 path 指定的文件存在,
os.path.lexists(path)
返回 true,无论它是否是符号链接。 这与现有的os.path.exists(path)
函数不同,如果 path 是指向不存在的目标的符号链接,则该函数返回 false。 (贝尼·切尔尼亚夫斯基供稿。)poplib 模块现在支持 POP over SSL。 (由赫克托·乌尔图比亚提供。)
profile 模块现在可以分析 C 扩展函数。 (由尼克巴斯汀提供。)
random 模块有一个名为
getrandbits(N)
的新方法,它返回一个长度为 N 位的长整数。 现有的randrange()
方法现在在适当的地方使用getrandbits()
,从而使任意大的随机数的生成更加高效。 (雷蒙德·赫廷格供稿。)re 模块接受的正则表达式语言被扩展为简单的条件表达式,写为
(?(group)A|B)
。 group 是数字组 ID 或在表达式前面用(?P<group>...)
定义的组名称。 如果指定的组匹配,则将针对字符串测试正则表达式模式 A; 如果组不匹配,则将使用模式 B。 (古斯塔沃·尼迈耶供稿。)由于 Gustavo Niemeyer 的大量工作,re 模块也不再是递归的。 在递归正则表达式引擎中,某些模式会导致消耗大量 C 堆栈空间,并且可能会溢出堆栈。 例如,如果将
a
字符的 30000 字节字符串与表达式(a|b)+
进行匹配,则每个字符消耗一个堆栈帧。 Python 2.3 尝试检查堆栈溢出并引发 RuntimeError 异常,但某些模式可能会回避检查,如果您不走运,Python 可能会出现段错误。 Python 2.4 的正则表达式引擎可以毫无问题地匹配这种模式。signal 模块现在对 signal.signal() 函数的参数执行更严格的错误检查。 例如,您不能在
SIGKILL
信号上设置处理程序; 以前版本的 Python 会悄悄地接受这一点,但 2.4 会引发 RuntimeError 异常。socket 模块中添加了两个新函数。
socketpair()
返回一对连接的套接字,getservbyport(port)
查找给定端口号的服务名称。 (由 Dave Cole 和 Barry Warsaw 提供。)sys.exitfunc()
函数已被弃用。 代码应该使用现有的 atexit 模块,它可以正确处理调用多个退出函数。 最终sys.exitfunc()
将成为纯粹的内部接口,只能通过 atexit 访问。tarfile 模块现在默认生成 GNU 格式的 tar 文件。 (拉尔斯·古斯塔贝尔供稿。)
threading 模块现在有一种优雅简单的方法来支持线程本地数据。 该模块包含一个
local
类,其属性值对于不同的线程是本地的。import threading data = threading.local() data.number = 42 data.url = ('www.python.org', 80)
其他线程可以为
number
和url
属性分配和检索它们自己的值。 您可以子类化local
来初始化属性或添加方法。 (由吉姆富尔顿提供。)timeit 模块现在会在计时循环期间自动禁用定期垃圾收集。 此更改使连续计时更具可比性。 (雷蒙德·赫廷格供稿。)
weakref 模块现在支持更多种类的对象,包括 Python 函数、类实例、集合、冻结集、双端队列、数组、文件、套接字和正则表达式模式对象。 (雷蒙德·赫廷格供稿。)
xmlrpclib
模块现在支持多调用扩展,用于在单个 HTTP 操作中传输多个 XML-RPC 调用。 (由布赖恩昆兰提供。)mpz
、rotor
和xreadlines
模块已被移除。
文档测试
doctest 模块在 Edward Loper 和 Tim Peters 的帮助下进行了大量重构。 测试仍然可以像运行 doctest.testmod() 一样简单,但重构允许以各种方式自定义模块的操作
新的 DocTestFinder
类从给定对象的文档字符串中提取测试:
def f (x, y):
""">>> f(2,2)
4
>>> f(3,2)
6
"""
return x*y
finder = doctest.DocTestFinder()
# Get list of DocTest instances
tests = finder.find(f)
新的 DocTestRunner
类然后运行单独的测试并可以生成结果摘要:
runner = doctest.DocTestRunner()
for t in tests:
tried, failed = runner.run(t)
runner.summarize(verbose=1)
上面的示例产生以下输出:
1 items passed all tests:
2 tests in f
2 tests in 1 items.
2 passed and 0 failed.
Test passed.
DocTestRunner
使用 OutputChecker
类的实例将预期输出与实际输出进行比较。 此类采用许多不同的标志来自定义其行为; 雄心勃勃的用户还可以编写一个全新的 OutputChecker
子类。
默认输出检查器提供了许多方便的功能。 例如,使用 doctest.ELLIPSIS 选项标志,预期输出中的省略号 (...
) 匹配任何子字符串,从而更容易适应以次要方式变化的输出:
def o (n):
""">>> o(1)
<__main__.C instance at 0x...>
>>>
"""
另一个特殊字符串 <BLANKLINE>
匹配空行:
def p (n):
""">>> p(1)
<BLANKLINE>
>>>
"""
另一个新功能是通过指定 doctest.REPORT_UDIFF(统一差异)、doctest.REPORT_CDIFF(上下文差异)或 doctest 来生成输出的差异样式显示。 REPORT_NDIFF (delta-style) 选项标志。 例如:
def g (n):
""">>> g(4)
here
is
a
lengthy
>>>"""
L = 'here is a rather lengthy list of words'.split()
for word in L[:n]:
print word
使用指定的 doctest.REPORT_UDIFF 运行上述函数的测试,您会得到以下输出:
**********************************************************************
File "t.py", line 15, in g
Failed example:
g(4)
Differences (unified diff with -expected +actual):
@@ -2,3 +2,3 @@
is
a
-lengthy
+rather
**********************************************************************
构建和 C API 更改
Python 的构建过程和 C API 的一些更改是:
- 为扩展函数的常见返回值添加了三个新的便利宏:Py_RETURN_NONE、Py_RETURN_TRUE 和 Py_RETURN_FALSE。 (由布雷特·坎农提供。)
- 另一个新宏 Py_CLEAR(obj) 减少了 obj 的引用计数并将 obj 设置为空指针。 (由吉姆富尔顿提供。)
- 一个新函数
PyTuple_Pack(N, obj1, obj2, ..., objN)
从 Python 对象的可变长度参数列表构造元组。 (雷蒙德·赫廷格供稿。) - 一个新函数
PyDict_Contains(d, k)
实现了快速字典查找,而不会屏蔽查找过程中引发的异常。 (雷蒙德·赫廷格供稿。) - 如果 Py_IS_NAN(X) 宏的浮点或双参数 X 是 NaN,则该宏返回 1。 (由蒂姆·彼得斯提供。)
- C 代码可以通过使用新的 PyEval_ThreadsInitialized() 函数来判断是否执行了任何线程操作,从而避免不必要的锁定。 如果此函数返回 false,则不需要锁定操作。 (由尼克·科格兰提供。)
- 新函数 PyArg_VaParseTupleAndKeywords() 与 PyArg_ParseTupleAndKeywords() 相同,但采用
va_list
而不是多个参数。 (由格雷格查普曼提供。) - 新的方法标志
METH_COEXISTS
允许在槽中定义的函数与具有相同名称的 PyCFunction 共存。 这可以将诸如set.__contains__()
之类的方法的访问时间减半。 (雷蒙德·赫廷格供稿。) - 现在可以使用解释器本身的额外分析来构建 Python,旨在帮助开发 Python 核心的人。 为 configure 脚本提供
--enable-profiling
将使您能够使用 gprof 对解释器进行分析,并提供--with-tsc
开关可以使用 Pentium 的时间戳进行分析- 柜台登记。 请注意,--with-tsc
开关的名称略有错误,因为分析功能也适用于 PowerPC 平台,尽管该处理器架构并未将该寄存器称为“TSC 寄存器”。 (由杰里米·希尔顿提供。) tracebackobject
类型已重命名为PyTracebackObject
。
特定于端口的更改
- Windows 端口现在在 MSVC++ 7.1 和版本 6 下构建。 (由 Martin von Löwis 提供。)
移植到 Python 2.4
本节列出了可能需要更改代码的先前描述的更改:
- 过大的左移和十六进制/八进制常量不再触发 FutureWarning 并返回限制为 32 或 64 位的值; 相反,它们返回一个长整数。
- 整数运算将不再触发
OverflowWarning
。OverflowWarning
警告将在 Python 2.5 中消失。 - zip() 内置函数和
itertools.izip()
现在返回一个空列表,而不是在不带参数调用时引发 TypeError 异常。 - 您无法再比较 datetime 模块提供的
date
和 datetime 实例。 不同类的两个实例现在总是不相等的,相对比较 (<
,>
) 将引发 TypeError。 dircache.listdir()
现在将异常传递给调用者而不是返回空列表。LexicalHandler.startDTD()
用于以错误的顺序接收公共和系统 ID。 这已得到纠正; 需要修复依赖错误顺序的应用程序。- fcntl.ioctl() 现在警告 mutate 参数是否被省略和相关。
- tarfile 模块现在默认生成 GNU 格式的 tar 文件。
- 导入模块时遇到失败不再在
sys.modules
中留下部分初始化的模块对象。 - None 现在是一个常数; 将新值绑定到名称
None
的代码现在是一个语法错误。 signals.signal()
函数现在针对某些非法值引发 RuntimeError 异常; 以前这些错误会默默传递。 例如,您不能再在SIGKILL
信号上设置处理程序。
致谢
作者要感谢以下人员对本文的各种草稿提供建议、更正和帮助:Koray Can、Hye-Shik Chang、Michael Dyck、Raymond Hettinger、Brian Hurt、Hamish Lawson、Fredrik Lundh、Sean Reifschneider、Sadruddin拒绝。