python 2.2的新功能

作者

库克林

介绍

本文解释了2002年10月14日发布的python 2.2.2中的新特性。python 2.2.2是python 2.2的错误修复版本,最初于2001年12月21日发布。

python 2.2可以被认为是“清理版本”。有些特性,比如生成器和迭代器,是全新的,但是大部分的变化,虽然意义重大,影响深远,但都是为了清除语言设计中的不规则和黑暗角落。

本文并不试图提供新特性的完整规范,而是提供一个方便的概述。有关完整的详细信息,您应该参考Python2.2的文档,例如 Python Library Reference 以及 Python Reference Manual . 如果您想了解变更的完整实现和设计原理,请参考PEP了解特定的新特性。

PEPS 252和253:类型和等级变更

python 2.2中最大和最深远的变化是对python的对象和类模型的更改。这些更改应该是向后兼容的,所以您的代码可能会继续保持不变的运行,但是这些更改提供了一些惊人的新功能。在开始这篇文章最长也是最复杂的部分之前,我将提供一个变更的概述并提供一些注释。

很久以前,我写了一个网页,列出了Python设计中的缺陷。其中一个最显著的缺陷是,不可能对C中实现的Python类型进行子类划分。特别是,不可能对内置类型进行子类划分,因此不能只对列表进行子类划分,以向它们添加一个有用的方法。这个 UserList 模块提供了一个类,它支持列表的所有方法,并且可以进一步进行子类化,但是有很多C代码需要一个常规的python列表,并且不接受 UserList 实例。

python 2.2修复了这个问题,并在这个过程中添加了一些令人兴奋的新功能。简要总结:

  • 您可以对内置类型(如列表甚至整数)进行子类划分,并且您的子类应该可以在需要原始类型的每个地方工作。

  • 现在可以定义静态方法和类方法,除了以前版本的Python中提供的实例方法之外。

  • 还可以使用一种新的机制来自动调用访问或设置实例属性的方法 properties . 多种用途 __getattr__() 可以重写以使用属性,从而使生成的代码更简单、更快。作为一个小的好处,属性现在也可以有docstring。

  • 实例的法律属性列表可以限制为使用 slots 使之能够防止输入错误,并可能在将来的Python版本中实现更多的优化。

一些用户对所有这些变化表示担忧。当然,他们说,这些新特性是整洁的,并且适合于各种各样的技巧,这些技巧在以前的Python版本中是不可能的,但是它们也使语言更加复杂。有些人说,他们总是推荐Python的简单性,并认为它的简单性正在丧失。

我个人认为没必要担心。许多新特性都非常深奥,您可以编写大量的Python代码,而无需了解它们。写一个简单的课程并不比以前难,所以你不需要费心学习或教他们,除非他们是真正需要的。以前只有C才能完成的一些非常复杂的任务现在可以在纯Python中完成,在我看来,这是更好的选择。

我不会试图覆盖每一个角落的情况和小的变化,需要使新的功能工作。相反,本节将只绘制宽笔画。见节 相关链接 “相关链接”,以获取有关Python2.2新对象模型的更多信息源。

旧类和新类

首先,您应该知道,python 2.2实际上有两种类:经典类或旧样式类,以及新样式类。旧样式的类模型与早期版本的Python中的类模型完全相同。本节中描述的所有新特性只适用于新样式的类。这种分歧并不打算永远持续下去;最终,旧样式的类将被删除,可能在Python3.0中。

那么,如何定义一个新的样式类呢?您可以通过对现有的新样式类进行子类化来实现这一点。现在,python的大多数内置类型(如整数、列表、字典甚至文件)都是新样式的类。一个名为 object 也添加了所有内置类型的基类,因此,如果没有合适的内置类型,则可以将其子类 object ::

class C(object):
    def __init__ (self):
        ...
    ...

这意味着 class 没有任何基类的语句总是Python2.2中的经典类。(实际上,您也可以通过设置一个名为 __metaclass__ ---见 PEP 253 对于细节——但是更容易将其子类化 object

内置类型的类型对象可用作内置类型,使用巧妙的技巧命名。python始终具有名为 int()float()str() . 在2.2中,它们不再起作用,而是在调用时作为工厂的类型对象。::

>>> int
<type 'int'>
>>> int('123')
123

要使类型集完整,请使用新的类型对象,例如 dict()file() 已添加。下面是一个更有趣的例子,添加了 lock() 文件对象的方法:

class LockableFile(file):
    def lock (self, operation, length=0, start=0, whence=0):
        import fcntl
        return fcntl.lockf(self.fileno(), operation,
                           length, start, whence)

现在已经过时的 posixfile 模块包含一个类,该类模拟了文件对象的所有方法,并添加了 lock() 方法,但无法将该类传递给期望有内置文件的内部函数,这是新的 LockableFile .

描述符

在以前的Python版本中,没有一致的方法来发现对象支持哪些属性和方法。有一些非正式的惯例,例如定义 __members____methods__ 属性是名称列表,但通常扩展类型或类的作者不会费心定义它们。你可以回去检查一下 __dict__ 对象,但当类继承或任意 __getattr__() 钩子在使用中,这仍然可能是不准确的。

新类模型的一个重要思想是,用于描述对象属性的API使用 descriptors 已经正式化了。描述符指定属性的值,说明它是方法还是字段。使用描述符API,静态方法和类方法成为可能,以及更为奇特的构造。

属性描述符是生活在类对象内部的对象,并且有一些自己的属性:

  • __name__ 属性的名称。

  • __doc__ 是属性的docstring。

  • __get__(object) 是从中检索属性值的方法 object .

  • __set__(object, value) 将属性设置为 objectvalue .

  • __delete__(object, value) 删除 value 属性 object .

例如,当你写 obj.x ,python实际执行的步骤是:

descriptor = obj.__class__.x
descriptor.__get__(obj)

对于方法, descriptor.__get__() 返回一个可调用的临时对象,并封装要对其调用的实例和方法。这也是为什么静态方法和类方法现在是可能的;它们有只包含方法或方法和类的描述符。作为对这些新方法的简要说明,静态方法不会传递实例,因此类似于正则函数。类方法传递给对象的类,而不是对象本身。静态方法和类方法的定义如下:

class C(object):
    def f(arg1, arg2):
        ...
    f = staticmethod(f)

    def g(cls, arg1, arg2):
        ...
    g = classmethod(g)

这个 staticmethod() 函数接受函数 f() ,并返回封装在描述符中的值,以便将其存储在类对象中。您可能期望有特殊的语法来创建这样的方法。 (def static fdefstatic f() 或者类似的东西)但是还没有定义这样的语法;这是留给将来的Python版本的。

更多的新特性,比如槽和属性,也被实现为新的描述符类型,并且编写一个能够完成一些新颖工作的描述符类并不困难。例如,可以编写一个描述符类,这样就可以为一个方法编写埃菲尔风格的前置条件和后置条件。使用此功能的类的定义如下:

from eiffel import eiffelmethod

class C(object):
    def f(self, arg1, arg2):
        # The actual function
        ...
    def pre_f(self):
        # Check preconditions
        ...
    def post_f(self):
        # Check postconditions
        ...

    f = eiffelmethod(f, pre_f, post_f)

请注意,使用新的 eiffelmethod() 不需要理解任何关于描述符的内容。这就是为什么我认为新特性不会增加语言的基本复杂性。会有一些巫师需要知道它才能写作 eiffelmethod() 或者是zodb或者其他什么,但是大多数用户只会在生成的库之上编写代码,而忽略实现细节。

多重继承:钻石法则

通过更改解析名称的规则,多重继承也变得更加有用。考虑这组类(图表取自 PEP 253 由Guido van Rossum)::

      class A:
        ^ ^  def save(self): ...
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def save(self): ...
     \       /
      \     /
       \   /
        \ /
      class D

经典类的查找规则很简单,但不是很聪明;基本类首先从左到右搜索深度。对…的参考 D.save() 将搜索类 DB 然后 A 在哪里 save() 会被找到并归还。 C.save() 根本找不到。这很糟糕,因为如果 Csave() 方法正在保存特定于 C ,不调用它将导致该状态永远无法保存。

新的样式类遵循一种不同的算法,这种算法解释起来有点复杂,但在这种情况下做的是正确的。(请注意,在大多数情况下,python 2.3将此算法更改为产生相同结果的算法,但对于非常复杂的继承图,会产生更有用的结果。)

  1. 按照经典查找规则列出所有的基类,如果重复访问某个类,则将其包括多次。在上面的示例中,访问的类的列表是 [D, B, A, C, A] .

  2. 扫描列表中的重复类。如果找到任何事件,则删除除一个事件以外的所有事件,保留 last 列表中的一个。在上面的示例中,列表变为 [D, B, C, A] 删除重复项后。

遵循此规则,指 D.save() 将返回 C.save() 这就是我们追求的行为。此查找规则与后面跟着公共lisp的查找规则相同。新的内置功能, super() ,提供了一种在不需要重新实现Python算法的情况下获取类超类的方法。最常用的形式是 super(class, obj) 返回绑定的超类对象(不是实际的类对象)。此窗体将在方法中用于调用超类中的方法;例如, Dsave() 方法如下所示:

class D (B,C):
    def save (self):
        # Call superclass .save()
        super(D, self).save()
        # Save D's private information here
        ...

super() 当作为调用时,还可以返回未绑定的超类对象 super(class)super(class1, class2) 但这可能不会经常有用。

属性访问

大量复杂的python类使用 __getattr__() ;最常见的做法是为了方便起见,通过自动映射属性访问(如 obj.parent 到方法调用中,例如 obj.get_parent . python 2.2增加了一些控制属性访问的新方法。

第一, __getattr__(attr_name) 仍然由新样式的类支持,并且没有任何变化。和以前一样,当试图访问时将调用它 obj.foo 没有指定的属性 foo 在实例的字典中找到。

新的样式类也支持新的方法, __getattribute__(attr_name) . 这两种方法的区别在于 __getattribute__()总是 每当访问任何属性时调用,而旧的 __getattr__() 仅在以下情况下调用 foo 在实例的字典中找不到。

但是,python 2.2支持 properties 通常是捕获属性引用的简单方法。写一篇 __getattr__() 方法很复杂,因为为了避免递归,不能在其中使用常规的属性访问,而是必须处理 __dict__ . __getattr__() 当Python检查其他方法(如 __repr__()__coerce__() 所以必须记住这一点。最后,对每个属性访问调用一个函数会导致相当大的性能损失。

property 是一种新的内置类型,它包含三个函数,分别获取、设置或删除属性和docstring。例如,如果要定义 size 属性可以计算,但也可以设置,您可以写:

class C(object):
    def get_size (self):
        result = ... computation ...
        return result
    def set_size (self, size):
        ... compute something based on the size
        and set internal state appropriately ...

    # Define a property.  The 'delete this attribute'
    # method is defined as None, so the attribute
    # can't be deleted.
    size = property(get_size, set_size,
                    None,
                    "Storage size of this instance")

这肯定比一对 __getattr__()/__setattr__() 用于检查 size 属性并在从实例的 __dict__ . 访问 size 也是唯一必须执行调用函数工作的属性,因此对其他属性的引用以它们通常的速度运行。

最后,可以使用新的 __slots__ 类属性。python对象通常是非常动态的;任何时候都可以通过执行 obj.new_attr=1 .新样式的类可以定义名为 __slots__ 将法律属性限制为一组特定的名称。一个例子将说明这一点:

>>> class C(object):
...     __slots__ = ('template', 'name')
...
>>> obj = C()
>>> print obj.template
None
>>> obj.template = 'Test'
>>> print obj.template
Test
>>> obj.newattr = None
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'newattr'

注意如何获得 AttributeError 尝试分配给未在中列出的属性时 __slots__ .

PEP234:迭代器

2.2的另一个重要补充是在C和Python级别上都有一个迭代接口。对象可以定义调用方如何循环它们。

在2.1之前的Python版本中,通常的方法是 for item in obj 工作是定义 __getitem__() 方法如下:

def __getitem__(self, index):
    return <next item>

__getitem__() 更适用于在对象上定义索引操作,以便您可以写入 obj[5] 检索第六个元素。当你用这个来支持 for 循环。考虑一些类似文件的对象,这些对象希望循环访问; index 参数本质上是无意义的,因为类可能假定 __getitem__() 调用将使用 index 每次递增一个。换句话说,存在于 __getitem__() 方法并不意味着使用 file[5] 随机访问第六个元素是可行的,尽管它确实应该如此。

在python 2.2中,迭代可以单独实现,并且 __getitem__() 方法可以限制为真正支持随机访问的类。迭代器的基本思想很简单。新的内置功能, iter(obj)iter(C, sentinel) ,用于获取迭代器。 iter(obj) 返回对象的迭代器 obj ,同时 iter(C, sentinel) 返回将调用可调用对象的迭代器 C 直到它回来 哨兵 表示迭代器已完成。

python类可以定义 __iter__() 方法,它应该为该对象创建并返回一个新的迭代器;如果该对象是它自己的迭代器,则此方法可以只返回 self . 特别是,迭代器通常是它们自己的迭代器。在C中实现的扩展类型可以实现 tp_iter 函数以返回迭代器,而希望表现为迭代器的扩展类型可以定义 tp_iternext 功能。

那么,在所有这些之后,迭代器实际上做什么呢?他们有一个必要的方法, next() ,它不接受参数并返回下一个值。当没有更多的值要返回时,调用 next() 应该提高 StopIteration 例外。::

>>> L = [1,2,3]
>>> i = iter(L)
>>> print i
<iterator object at 0x8116870>
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
StopIteration
>>>

在2.2中,python的 for 语句不再需要序列;它需要 iter() 将返回迭代器。为了向后兼容和方便起见,自动为未实现的序列构造迭代器 __iter__() 或A tp_iter 槽,所以 for i in [1,2,3] 仍然有效。只要python解释器循环一个序列,它就会被更改为使用迭代器协议。这意味着你可以这样做:

>>> L = [1,2,3]
>>> i = iter(L)
>>> a,b,c = i
>>> a,b,c
(1, 2, 3)

迭代器支持已经添加到了一些Python的基本类型中。调用 iter() 在字典上,将返回循环其键的迭代器:

>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
...      'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
>>> for key in m: print key, m[key]
...
Mar 3
Feb 2
Aug 8
Sep 9
May 5
Jun 6
Jul 7
Jan 1
Apr 4
Nov 11
Dec 12
Oct 10

这只是默认行为。如果要在键、值或键/值对上迭代,可以显式调用 iterkeys()itervalues()iteritems() 方法获取适当的迭代器。在较小的相关变化中, in 操作员现在在查字典,所以 key in dict 现在等于 dict.has_key(key) .

文件还提供迭代器,它调用 readline() 方法,直到文件中不再有行。这意味着您现在可以使用如下代码读取文件的每一行:

for line in file:
    # do something for each line
    ...

请注意,只能在迭代器中前进;无法获取前一个元素、重置迭代器或复制它。迭代器对象可以提供这样的附加功能,但是迭代器协议只需要 next() 方法。

参见

PEP 234 迭代器

由Ka Ping Yee和Gvr编写;由python labs团队实施,主要由Gvr和Tim Peters完成。

PEP 255:简单生成器

生成器是另一个新特性,它与迭代器的引入交互。

毫无疑问,您对函数调用在python或c中的工作方式非常熟悉。当您调用一个函数时,它会得到一个私有的命名空间,在其中创建局部变量。当函数达到 return 语句,局部变量将被销毁,结果值将返回给调用方。稍后对同一函数的调用将获得一组新的局部变量。但是,如果局部变量在退出函数时没有被丢弃呢?如果你以后能恢复它停止的功能呢?这就是生成器提供的;它们可以被认为是可恢复的功能。

以下是生成器函数的最简单示例:

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

一个新的关键字, yield ,用于生成器。任何包含 yield 语句是一个生成器函数;这是由专门编译函数的Python字节码编译器检测到的。因为引入了一个新的关键字,所以必须在模块中通过包含 from __future__ import generators 靠近模块源代码顶部的语句。在Python2.3中,这个语句将变得不必要。

当调用生成器函数时,它不会返回单个值;而是返回支持迭代器协议的生成器对象。关于执行 yield 语句,生成器输出 i ,类似于 return 语句。两者之间的巨大区别 yield 和A return 声明是在达到 yield 生成器的执行状态被挂起,局部变量被保留。下一次调用生成器时 next() 方法,函数将在 yield 语句。(由于复杂的原因, yield 语句不允许包含在 try A块 try …… finally 语句;读取 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$Queens放在$NXN$Chess板上,这样就不会有皇后威胁到其他人)和骑士之旅(一条路线可以让骑士不经过任何广场两次就到达$NXN$ChessBoard的每一个广场)。

生成器的概念来自其他编程语言,特别是icon(https://www.cs.alizarian.edu/icon/),其中生成器的概念是核心。在icon中,每个表达式和函数调用的行为都像一个生成器。https://www.cs.alizarian.edu/icon/docs/ipd266.htm上的“图标编程语言概述”中的一个示例给出了以下情况的概念:

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

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

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

参见

PEP 255 -简单生成器

作者:Neil Schemenauer,Tim Peters,Magnus Lie Hetland。主要由Neil Schemenauer和Tim Peters实施,其他修复来自Python实验室的工作人员。

PEP 237:统一长整数和整数

在最近的版本中,常规整数(大多数机器上为32位值)和长整数(可以是任意大小)之间的区别变得很烦人。例如,在支持大于 2**32 字节, tell() 文件对象的方法必须返回一个长整数。但是,python中有许多位需要纯整数,如果提供长整数,则会引发错误。例如,在python 1.5中,只有正则整数可以用作切片索引,并且 'abc'[1L:] 会提高 TypeError 消息“slice index must be int”出现异常。

python 2.2将根据需要将值从短整型转换为长整型。不再需要“l”后缀来表示长整型文字,因为现在编译器将选择适当的类型。(在未来的2.x版本的python中,不鼓励使用“l”后缀,这会在python 2.4中触发警告,并可能会在python 3.0中删除。)许多用于引发 OverflowError 现在将返回一个长整数作为结果。例如::

>>> 1234567890123
1234567890123L
>>> 2 ** 64
18446744073709551616L

在大多数情况下,整数和长整数现在将被相同地处理。你仍然可以用 type() 内置功能,但很少需要。

参见

PEP 237 -统一长整数和整数

由Moshe Zadka和Guido van Rossum撰写。主要由Guido van Rossum实施。

PEP 238:更改除法运算符

python 2.2中最具争议的变化预示着修复一个从一开始就存在于python中的旧设计缺陷的努力的开始。目前,python的部门运营商, / ,在有两个整数参数时,其行为类似于C的除法运算符:它返回一个整数结果,当有小数部分时,该结果被截断。例如, 3/2 是1,而不是1.5,并且 (-1)/2 是-1,而不是-0.5。这意味着,根据两个操作数的类型,除法的结果可能会发生意外的变化,而且由于python是动态类型的,因此很难确定可能的操作数类型。

(参数的焦点是这是否是 真正地 一个设计缺陷,以及是否值得打破现有的代码来解决这个问题。它引起了关于python dev的无休止的讨论,2001年7月爆发了一场激烈的讽刺性帖子 comp.lang.python . 我不会在这里为任何一方参数,我会坚持描述2.2中实现的内容。读 PEP 238 用于参数和反参数的摘要。)

因为这个更改可能会破坏代码,所以正在逐步引入它。python 2.2开始转换,但直到python 3.0才完成转换。

首先,我将借用一些术语 PEP 238 . "真正的除法”是大多数非程序员都熟悉的除法:3/2是1.5,1/4是0.25,等等。“向下取整数”是 Python 的 / 当给定整数操作数时,运算符当前执行此操作;结果是真除法返回的值的下限。”经典的划分“是目前混合的行为 / ;当操作数为整数时返回底除结果,当其中一个操作数为浮点数时返回真除结果。

以下是2.2所做的更改:

  • 一个新的算符, // ,是向下取整数操作员。(是的,我们知道它看起来像C++的注释符号。) // 总是 不管操作数的类型是什么,都执行向下取整数,因此 1 // 2 是0和 1.0 // 2.0 也是0。

    // 在python 2.2中始终可用;您不需要使用 __future__ 语句。

  • 通过包括 from __future__ import division in a module, the / operator will be changed to return the result of true division, so 1/2 为 0.5。没有 __future__ statement, / still means classic division. The default meaning of / 在python 3.0之前不会更改。

  • 类可以定义调用的方法 __truediv__()__floordiv__() 使两个除法运算符过载。在C层,也有槽在 PyNumberMethods 结构,以便扩展类型可以定义这两个运算符。

  • python 2.2支持一些命令行参数,用于测试代码是否可以使用更改后的除法语义。运行python -Q warn 当除法应用于两个整数时,将导致发出警告。您可以使用它来查找受更改影响的代码并修复它。默认情况下,python 2.2只执行经典除法而不发出警告;在python 2.3中,默认情况下会打开警告。

参见

PEP 238 -更改除法运算符

由Moshe Zadka和Guido van Rossum撰写。由Guido van Rossum实施。

Unicode更改

python的unicode支持在2.2中得到了一些增强。Unicode字符串通常存储为ucs-2,即16位无符号整数。通过提供 --enable-unicode=ucs4 配置脚本。(也可以指定 --disable-unicode 完全禁用Unicode支持。)

当编译为使用ucs-4(“宽python”)时,解释器可以本地处理U+000000到U+110000之间的Unicode字符,因此 unichr() 相应地扩展函数。使用编译为使用ucs-2(“窄python”)的解释器,大于65535的值仍将导致 unichr() 养一个 ValueError 例外。这些都在 PEP 261 “支持‘宽’Unicode字符”;有关详细信息,请参阅它。

另一个变化更容易解释。自其引入以来,Unicode字符串支持 encode() 方法将字符串转换为选定的编码,如utf-8或Latin-1。对称的 decode([*encoding*]) 方法已添加到2.2中的8位字符串(但不是Unicode字符串)。 decode() 假定字符串是指定的编码并对其进行解码,返回编解码器返回的内容。

使用这个新功能,已经为与Unicode不直接相关的任务添加了编解码器。例如,为UU编码、mime的base64编码和使用 zlib 模块:

>>> s = """Here is a lengthy piece of redundant, overly verbose,
... and repetitive text.
... """
>>> data = s.encode('zlib')
>>> data
'x\x9c\r\xc9\xc1\r\x80 \x10\x04\xc0?Ul...'
>>> data.decode('zlib')
'Here is a lengthy piece of redundant, overly verbose,\nand repetitive text.\n'
>>> print s.encode('uu')
begin 666 <data>
M2&5R92!I<R!A(&QE;F=T:'D@<&EE8V4@;V8@<F5D=6YD86YT+"!O=F5R;'D@
>=F5R8F]S92P*86YD(')E<&5T:71I=F4@=&5X="X*

end
>>> "sheesh".encode('rot-13')
'furrfu'

要将类实例转换为Unicode,请执行以下操作: __unicode__() 方法可以由类定义,类似于 __str__() .

encode()decode()__unicode__() 由Marc Andr_Lemburg实施。弗雷德里克·伦德和马丁·冯·L_维斯在内部实施了对使用UCS-4进行支持的更改。

参见

PEP 261 -支持“宽”Unicode字符

保罗·普雷斯科特写的。

PEP 227:嵌套范围

在python 2.1中,静态嵌套的作用域作为可选功能添加,由 from __future__ import nested_scopes 指令。在2.2中,嵌套作用域不再需要特别启用,并且现在总是存在。本节的其余部分是我的“What's New in python 2.1”文档中嵌套范围描述的副本;如果在2.1出现时阅读它,可以跳过本节的其余部分。

python 2.1中引入的并在2.2中完成的最大变化是对python的作用域规则的更改。在Python2.0中,在任何给定的时间内,最多有三个名称空间用于查找变量名:本地名称空间、模块级名称空间和内置名称空间。这常常让人们感到惊讶,因为它不符合他们的直觉预期。例如,嵌套递归函数定义不起作用:

def f():
    ...
    def g(value):
        ...
        return g(value-1) + 1
    ...

函数 g() 将始终引发 NameError 异常,因为名称的绑定 g 不在其本地命名空间或模块级命名空间中。这在实践中并不是什么问题(像这样递归定义内部函数的频率是多少?),但这也使得 lambda 表达更笨拙,这在实践中是个问题。在使用的代码中 lambda 通过将局部变量作为参数的默认值传递,通常可以找到正在复制的局部变量。::

def find(self, name):
    "Return list of any entries equal to 'name'"
    L = filter(lambda x, name=name: x == name,
               self.list_attribute)
    return L

因此,以强函数风格编写的python代码的可读性受到很大影响。

对python 2.2最显著的改变是,静态作用域已经添加到语言中以解决这个问题。作为第一个效果, name=name 在上面的例子中,默认参数现在是不必要的。简单地说,当一个给定的变量名没有在一个函数中赋值时(通过赋值,或 defclassimport 语句),将在封闭范围的本地命名空间中查找对变量的引用。在PEP中可以找到对规则的更详细的解释和对实现的剖析。

此更改可能会导致代码出现一些兼容性问题,在该代码中,模块级和包含进一步函数定义的函数中都使用了相同的变量名作为局部变量。但这似乎不太可能,因为这样的代码一开始读起来会很混乱。

变化的一个副作用是 from module import *exec 在某些情况下,语句在函数范围内是非法的。python参考手册一直都这么说 from module import * 只在模块的顶层是合法的,但是cpython解释器以前从未强制过这一点。作为嵌套作用域实现的一部分,将Python源代码转换为字节码的编译器必须生成不同的代码来访问包含作用域中的变量。 from module import *exec 使编译器不可能弄清楚这一点,因为它们向本地命名空间添加在编译时不可知的名称。因此,如果函数包含函数定义或 lambda 带有自由变量的表达式,编译器将通过引发 SyntaxError 例外。

为了让前面的解释更清楚一点,下面是一个例子:

x = 1
def f():
    # The next line is a syntax error
    exec 'x=2'
    def g():
        return x

第4行包含 exec 语句是语法错误,因为 exec 将定义一个名为 x 其值应由 g() .

这不应该有太大的限制,因为 exec 在大多数Python代码中很少使用(而且在使用时,它通常是糟糕设计的标志)。

参见

PEP 227 -静态嵌套作用域

由Jeremy Hylton编写并实施。

新模块和改进模块

  • 这个 xmlrpclib 模块由Fredrik Lundh提供给标准库,为编写XML-RPC客户端提供支持。XML-RPC是一种基于HTTP和XML的简单远程过程调用协议。例如,下面的代码片段从O'Reilly网络中检索RSS频道列表,然后为一个频道列出最近的标题:

    import xmlrpclib
    s = xmlrpclib.Server(
          'http://www.oreillynet.com/meerkat/xml-rpc/server.php')
    channels = s.meerkat.getChannels()
    # channels is a list of dictionaries, like this:
    # [{'id': 4, 'title': 'Freshmeat Daily News'}
    #  {'id': 190, 'title': '32Bits Online'},
    #  {'id': 4549, 'title': '3DGamers'}, ... ]
    
    # Get the items for one channel
    items = s.meerkat.getItems( {'channel': 4} )
    
    # 'items' is another list of dictionaries, like this:
    # [{'link': 'http://freshmeat.net/releases/52719/',
    #   'description': 'A utility which converts HTML to XSL FO.',
    #   'title': 'html2fo 0.3 (Default)'}, ... ]
    

    这个 SimpleXMLRPCServer 模块使创建简单的XML-RPC服务器变得容易。有关XML-RPC的详细信息,请参阅http://xml rpc.scripting.com/。

  • 新的 hmac 模块实现了描述的HMAC算法 RFC 2104 . (由Gerhard H_ring贡献)

  • 最初返回长元组的几个函数现在返回的伪序列的行为与元组类似,但也有助于记忆的属性,如memberstu mtime或 tm_year . 增强功能包括 stat()fstat()statvfs()fstatvfs()os 模块,以及 localtime()gmtime()strptime()time 模块。

    例如,要使用旧的元组获取文件的大小,您最终会编写如下内容 file_size = os.stat(filename)[stat.ST_SIZE] 但是现在这可以写得更清楚了 file_size = os.stat(filename).st_size .

    此功能的原始补丁由Nick Mathewson提供。

  • python profiler已被广泛地重新编写,其输出中的各种错误也已得到纠正。(由小弗雷德·L·德雷克和蒂姆·彼得斯提供。)

  • 这个 socket 可以编译模块以支持IPv6;请指定 --enable-ipv6 python配置脚本的选项。(作者:Jun Ichiro“Itojun”Hagino)

  • 两个新的格式字符被添加到 struct 支持C的平台上的64位整数模块 long long 类型。 q 用于有符号的64位整数,并且 Q 是一个未签名的。该值以python的long integer类型返回。(蒂姆·彼得斯供稿)

  • 在解释器的交互模式中,有一个新的内置功能 help() 使用 pydoc python 2.1中引入的模块,用于提供交互式帮助。 help(object) 显示有关的任何可用帮助文本 object . help() 如果没有参数,您将进入联机帮助实用程序,在该实用程序中可以输入函数、类或模块的名称,以读取它们的帮助文本。(由Guido van Rossum用Ka Ping Yee的 pydoc 模块。

  • 对基于 re 模块。例如, re.sub()re.split() 函数已在C语言中重写。另一个补丁将某些Unicode字符范围的速度提高了2倍,并新增了一个 finditer() 方法,返回给定字符串中所有不重叠匹配项的迭代器。(SRE由Fredrik Lundh维护。BigCharset补丁由Martin von L_wis提供。)

  • 这个 smtplib 模块现在支持 RFC 2487 “通过tls保护SMTP”,因此现在可以加密python程序和传递消息的邮件传输代理之间的SMTP通信。 smtplib 还支持SMTP身份验证。(由Gerhard H_ring贡献)

  • 这个 imaplib 模块由Piers Lauder维护,支持几个新扩展:中定义的命名空间扩展 RFC 2342 ,sort,getacl和setacl。(安东尼·巴克斯特和米歇尔·佩尔蒂埃供稿)

  • 这个 rfc822 模块的电子邮件地址解析现在符合 RFC 2822 ,更新到 RFC 822 . (模块名称为 not 将被更改为 rfc2822 新封装, email ,还添加了用于分析和生成电子邮件的。(由巴里·华沙撰稿,出自他对邮递员的研究。)

  • 这个 difflib 模块现在包含一个新的 Differ 类,用于在两行文本之间生成人类可读的更改列表(“delta”)。还有两个生成器功能, ndiff()restore() 分别从两个序列返回一个delta,或者从一个delta返回一个原始序列。(大卫·古德,蒂姆·彼得斯的ndiff.py代码中的大卫·古德贡献的咕噜声,然后他进行了生成。)

  • 新常量 ascii_lettersascii_lowercaseascii_uppercase 添加到 string 模块。标准库中有几个模块使用 string.letters 是指范围a-z a-z,但当使用区域设置时,这种假设是不正确的,因为 string.letters 取决于当前区域设置定义的一组法律字符。小车模块都已修复,可以使用。 ascii_letters 相反。(由一个不知名的人报告;由小弗雷德L.德雷克修正)

  • 这个 mimetypes 模块现在通过添加一个 MimeTypes 类,它获取要分析的文件名列表。(小弗雷德·L·德雷克供稿)

  • A Timer 类已添加到 threading 允许在将来某个时间安排活动的模块。(由Itamar Shtull Trauring提供。)

解释程序更改和修复

有些更改只影响在C级别处理Python解释器的人员,因为他们正在编写Python扩展模块、嵌入解释器或只是对解释器本身进行黑客攻击。如果您只编写Python代码,这里描述的任何更改都不会对您产生很大影响。

  • 分析和跟踪函数现在可以在C中实现,C的运行速度比基于Python的函数快得多,并且应该减少分析和跟踪的开销。这将是Python开发环境的作者感兴趣的。在python的api中添加了两个新的c函数, PyEval_SetProfile()PyEval_SetTrace() . 现有的 sys.setprofile()sys.settrace() 函数仍然存在,只是被更改为使用新的C级接口。(小弗雷德·L·德雷克供稿)

  • 添加了另一个低级API,主要是Python调试器和开发工具的实现人员感兴趣的。 PyInterpreterState_Head()PyInterpreterState_Next() 让调用者遍历所有现有的解释器对象; PyInterpreterState_ThreadHead()PyThreadState_Next() 允许循环给定解释器的所有线程状态。(大卫·比兹利供稿)

  • 垃圾收集器的C级接口已被更改,以便更容易地编写支持垃圾收集的扩展类型并调试函数的误用。各种函数的语义略有不同,因此必须重命名一系列函数。使用旧API的扩展仍将编译,但 not 参与垃圾收集,因此为2.2更新它们应该被视为相当高的优先级。

    要将扩展模块升级到新的API,请执行以下步骤:

  • 重命名 Py_TPFLAGS_GC()PyTPFLAGS_HAVE_GC() .

  • 使用 PyObject_GC_New()PyObject_GC_NewVar() 分配

    对象,以及 PyObject_GC_Del() 解除分配。

  • 重命名 PyObject_GC_Init()PyObject_GC_Track()

    PyObject_GC_Fini()PyObject_GC_UnTrack() .

  • 去除 PyGC_HEAD_SIZE() 从对象大小计算。

  • 删除调用 PyObject_AS_GC()PyObject_FROM_GC() .

  • 一个新的 et 格式序列已添加到 PyArg_ParseTuple()et 同时使用参数和编码名称,如果参数原来是Unicode字符串,则将参数转换为给定的编码;如果参数是8位字符串,则将其单独保留,假定它已经处于所需的编码中。这与 es 格式字符,它假定8位字符串采用Python的默认ASCII编码,并将其转换为指定的新编码。(由M.-A.Lemburg提供,并用于以下部分所述的Windows上的MBCS支持。)

  • 另一个参数解析函数, PyArg_UnpackTuple() 已经增加了,这更简单,可能更快。调用者不指定格式字符串,只提供所需的最小和最大参数数,以及一组指向 PyObject* 将用参数值填充的变量。

  • 两个新flag METH_NOARGSMETH_O 在方法定义表中可用于简化没有参数或单个非类型化参数的方法的实现。调用此类方法比调用使用 METH_VARARGS .还有,老的 METH_OLDARGS 写C方法的风格现在被正式否决了。

  • 两个新的封装函数, PyOS_snprintf()PyOS_vsnprintf() 为相对较新的 snprintf()vsnprintf() C ILB API。与标准相比 sprintf()vsprintf() 函数中,python版本检查用于防止缓冲区溢出的缓冲区边界。(M.-A.Lemburg供稿)

  • 这个 _PyTuple_Resize() 函数丢失了一个未使用的参数,因此现在它使用2个参数而不是3个。第三个参数从未使用过,在将代码从早期版本移植到python 2.2时可以简单地丢弃它。

其他更改和修复

像往常一样,在源代码树中散布着许多其他的改进和错误修复。通过对cvs变更日志的搜索发现,在python 2.1和2.2之间应用了527个补丁,修复了683个bug;2.2.1应用了139个补丁,修复了143个bug;2.2.2应用了106个补丁,修复了82个bug。这些数字可能被低估了。

一些更显著的变化是:

  • jack jansen维护的用于python的macos端口的代码现在保存在主python cvs树中,并且已经对支持macos x做了许多更改。

    最重要的变化是能够将python构建为框架,通过提供 --enable-framework 编译python时配置脚本的选项。根据JackJansen的说法,“这将安装一个独立的python安装以及OS X框架”粘合到 /Library/Frameworks/Python.framework (或其他选择地点)。目前,这几乎没有什么直接的额外好处(实际上,有一个缺点是您必须更改路径才能找到python),但它是创建完整的python应用程序、移植macpython ide、可能将python用作标准OSA脚本语言等等的基础。”

    大多数Macpython工具箱模块(与MacOS API接口,如窗口、QuickTime、脚本等)都已移植到OS X,但在 setup.py . 想要尝试这些模块的人可以手动取消对它们的注释。

  • 传递给不接受它们的内置函数的关键字参数现在会导致 TypeError 将引发异常,并显示消息“ function 不接受关键字参数”。

  • 在python 2.1中作为扩展模块添加的弱引用现在是核心的一部分,因为它们被用于新样式类的实现中。这个 ReferenceError 因此,异常已从 weakref 模块将成为内置异常。

  • 一个新的剧本, Tools/scripts/cleanfuture.py 由蒂姆·彼得斯自动删除过时的 __future__ 来自python源代码的语句。

  • 额外的 flags 参数已添加到内置函数 compile() 因此 __future__ 现在可以在模拟的shell中正确地观察语句,例如由空闲环境和其他开发环境提供的语句。这在 PEP 264 . (迈克尔·哈德森供稿)

  • python 1.6引入的新许可证与gpl不兼容。这是通过对2.2许可证的一些小的文本更改来修复的,所以现在将Python再次嵌入到GPLED程序中是合法的。注意,python本身不是gpled,而是在一个基本上等同于BSD许可的许可下,与以前一样。许可证更改也应用于python 2.0.1和2.1.1版本。

  • 当在Windows上显示Unicode文件名时,python现在将其转换为mbcs编码的字符串,就像Microsoft文件API所使用的那样。由于文件API显式地使用了MBC,所以python选择ascii作为默认编码是一种麻烦。在Unix上,如果 locale.nl_langinfo(CODESET) 是可用的。(Windows支持由Mark Hammond在Marc Andr_Lemburg的帮助下提供。Unix支持由Martin von L_wis添加。)

  • 现在在Windows上启用了大文件支持。(蒂姆·彼得斯供稿)

  • 这个 Tools/scripts/ftpmirror.py 脚本现在解析 .netrc 文件,如果你有。(麦克·隆伯格供稿)

  • 返回的对象的某些功能 xrange() 函数现在已被弃用,并在访问时触发警告;它们将在Python2.3中消失。 xrange 对象试图通过支持切片、序列乘法和 in 操作员,但这些功能很少使用,因此有缺陷。这个 tolist() 方法与 startstopstep 属性也被弃用。在C级,第四个参数 PyRange_New() 函数, repeat ,也已弃用。

  • 字典实现有很多补丁,主要是为了修复潜在的核心转储,如果一个字典包含的对象偷偷地改变了hash值,或者改变了它们所包含的字典。有一段时间,python dev陷入了一种温和的节奏中,michael hudson找到了一个丢弃core的案例,tim peters修复了这个bug,michael找到了另一个案例,然后一个接一个地执行。

  • 在Windows上,由于StephenHansen提供了许多补丁,现在可以使用BorlandC编译python,尽管结果还没有完全发挥作用。(但是这个 is 进步…

  • 另一个Windows增强:WISE解决方案慷慨地为pythonlabs提供了安装程序Master 8.1系统的使用。早期的pythonlabs Windows安装程序使用了wise 5.0a,它开始显示它的年龄。(提姆·彼得斯封装)

  • 文件结束 .pyw 现在可以在Windows上导入。 .pyw 是一个仅限Windows的东西,用于指示需要使用pythonw.exe而不是python.exe来运行脚本,以防止出现DOS控制台来显示输出。这个补丁可以导入这样的脚本,以防它们也可以作为模块使用。(由David Bolen实施)

  • 在Python使用C的平台上 dlopen() 函数加载扩展模块,现在可以设置 dlopen() 使用 sys.getdlopenflags()sys.setdlopenflags() 功能。(由Bram Stolk提供。)

  • 这个 pow() 当提供浮点数时,内置函数不再支持3个参数。 pow(x, y, z) 返回 (x**y) % z 但这对浮点数永远不会有用,最终结果会根据平台的不同而不可预测地变化。像这样的调用 pow(2.0, 8.0, 7.0) 现在将提高 TypeError 例外。

确认

作者要感谢以下人士对本文的各种草稿提出建议、更正和帮助:弗雷德·布雷默、基思·布里格斯、安德鲁·达尔克、弗雷德·L·德雷克、小卡雷尔·费林格、大卫·古德、马克·哈蒙德、斯蒂芬·汉森、迈克尔·哈德森、杰克·詹森、马克·安德尔·莱姆伯格、马丁·冯·L·维斯、弗雷德里克·伦德,迈克尔·麦克雷、尼克·马修森、保罗·摩尔、古斯塔沃·尼迈耶、唐·奥唐纳、乔纳斯·帕拉斯马、蒂姆·彼得斯、詹斯·夸德、汤姆·莱因哈特、尼尔·舍曼纽尔、吉多·范·罗森、格雷格·沃德、爱德华·韦伯恩。