What’s New in Python 2.2

作者:上午。 Kuchling

Introduction

本文解释了2002年10月14日发布的Python 2.2.2的新功能。Python 2.2.2是Python 2.2的bug修复版本,最初于2001年12月21日发布。

Python 2.2可以被认为是“清理版本”。有一些功能,如生成器和迭代器是完全新的,但大部分的变化,意义重大和影响深远,虽然他们可能是,旨在清理不规则和语言设计的黑暗角落。

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

PEPs 252 and 253: Type and Class Changes

Python 2.2中最大和最深远的变化是Python的对象和类的模型。更改应该向后兼容,因此您的代码可能会继续运行不变,但更改提供了一些了不起的新功能。在开始之前,这篇文章中最长和最复杂的部分,我将提供对这些更改的概述,并提供一些意见。

很久以前,我写了一个网页列出了Python的设计中的缺陷。最重要的缺陷之一是,它不可能子类化在C中实现的Python类型。特别是,不可能子类化内建类型,所以你不能只是子类,例如列表,以添加一个有用的方法他们。UserList模块提供了一个类,它支持所有列表的方法,并且可以进一步子类化,但有很多C代码需要一个普通的Python列表,不接受UserList实例。

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

  • 你可以继承内建类型,如列表甚至整数,你的子类应该在需要原始类型的每个地方工作。
  • 现在可以定义静态和类方法,除了在以前的Python版本中可用的实例方法。
  • 还可以通过使用称为属性的新机制来自动调用访问或设置实例属性的方法。可以重写__getattr__()的许多用法来使用属性,从而使得到的代码更简单和更快。作为一个小的好处,属性现在也可以有docstrings。
  • 实例的法律属性列表可以使用插槽限制为特定集合,从而可以防止打印错误,并且可能在将来的Python版本中进行更多优化。

一些用户对所有这些更改都表示关注。当然,他们说,新的功能是整洁的,并借助各种各样的技巧,在以前的Python版本是不可能的,但他们也使语言更复杂。有些人说他们总是推荐Python的简单性,并觉得它的简单性正在失去。

就个人而言,我认为没有必要担心。许多新功能都非常深奥,你可以编写很多Python代码,而不需要知道它们。写一个简单的类并不比以前更困难,所以你不需要麻烦学习或教他们,除非他们实际需要。一些非常复杂的任务,以前只能从C可能在纯Python,并在我的头脑,这是所有的更好。

我不打算尝试覆盖每一个角落情况和使新功能工作所需的小改动。相反,此部分将仅绘制宽行程。有关Python 2.2的新对象模型的更多信息源,请参阅Related Links,“相关链接”。

Old and New Classes

首先,你应该知道Python 2.2真的有两种类:经典或旧式类和新式类。旧样式类模型与早期版本的Python中的类模型完全相同。本节中描述的所有新功能仅适用于新式类。这种差异并不是永远持续的;最终老式的类将被删除,可能在Python 3.0。

那么如何定义一个新式类呢?你可以通过继承一个现有的新式样的类。大多数Python的内建类型,如整数,列表,字典,甚至文件,现在是新式的类。还添加了一个名为object的新样式类,即所有内建类型的基类,如果没有内建类型是合适的,则只需要子类化object

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

这意味着没有任何基类的class语句在Python 2.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可能的功能。

Descriptors

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

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

属性描述器是存在于类对象中的对象,并且具有自己的一些属性:

  • __name__是属性的名称。
  • __doc__是属性的docstring。
  • __get__(object)是从对象中检索属性值的方法。
  • __ set __(object, 值)对象上的属性设置为
  • __ delete __(对象, 值)删除对象属性。

例如,当你写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 f defstatic 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或任何,但大多数用户只是写的代码在结果库的顶部,并忽略实现细节。

Multiple Inheritance: The Diamond Rule

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

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

经典类的查找规则很简单,但不是很聪明;基本类是深度优先搜索,从左到右。A reference to D.save() will search the classes D, B, and then A, where save() would be found and returned. C.save()永远不会被找到。这是坏的,因为如果Csave()方法保存特定于C的一些内部状态,不调用它将导致状态从未得到保存。

新式类遵循不同的算法,这有点复杂,但解释,但在这种情况下做正确的事情。(请注意,Python 2.3会将此算法更改为在大多数情况下生成相同结果的算法,但会为真正复杂的继承图生成更有用的结果。)

  1. 列出所有基类,遵循经典查找规则,如果重复访问,则包含一个类多次。在上述示例中,访问类的列表是[DBACA]。
  2. 扫描列表以查找重复的类。如果发现任何一个,则删除除了一个事件之外的所有事件,留下列表中的最后在上述示例中,在删除重复项之后,列表变为[DBCA]。

遵循这个规则,参考D.save()将返回C.save(),这是我们后面的行为。此查找规则与Common 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)时返回未绑定的超类对象。 ,但这可能不会很有用。

Attribute Access

相当多的复杂的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对属性的支持通常是一种更简单的方法来捕获属性引用。写一个__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")

这比确定size属性并处理它的__getattr__() / __setattr__()方法更清楚和更容易写特别是在从实例的__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'

请注意,如果尝试分配到__slots__中未列出的属性,您是如何得到AttributeError的。

PEP 234: Iterators

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

In Python versions up to 2.1, the usual way to make for item in obj work is to define a __getitem__() method that looks something like this:

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

__getitem__()更适合用于定义对象的索引操作,以便您可以写入obj[5]来检索第六个元素。当你使用它只支持for循环时,它有点误导。考虑一些想要被循环的类文件对象; 索引参数基本上没有意义,因为类可能假设将使用索引递增一个 p>来进行一系列__getitem__()换句话说,__getitem__()方法的存在并不意味着使用file[5]随机访问第六个元素将起作用,尽管它真的应该。

在Python 2.2中,迭代可以单独实现,__getitem__()方法可以限制为真正支持随机访问的类。迭代器的基本思想很简单。新的内建函数iter(obj)iter(C, sentinel)迭代器。iter(obj)返回对象obj的迭代器,而iter(C, sentinel) 返回一个迭代器,它将调用可调用对象C,直到返回sentinel,表示迭代器完成。

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__()tp_iter时隙的序列自动构造迭代器,因此t7> 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操作符现在工作在字典,因此 t>现在等效于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: Simple Generators

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

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

这里是生成器函数的最简单的例子:

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

为生成器引入了一个新关键字yield包含yield语句的任何函数都是生成器函数;这是由Python的字节码编译器检测的,它特别编译了函数作为结果。由于引入了新关键字,必须在模块中显式启用生成器,方法是在之前加入 __ future __ import 语句位于模块源代码顶部。在Python 2.3中,这个语句将变得不必要。

当调用生成器函数时,它不返回单个值;而是返回一个支持迭代器协议的生成器对象。在执行yield语句时,生成器输出i的值,类似于return语句。yieldreturn语句之间的巨大差异是,在达到yield时,生成器的执行状态被暂停,并且保留局部变量。在下一次调用生成器的next()方法时,函数将在yield语句之后立即恢复执行。(由于复杂的原因,在try ... finallytry块中不允许yield >语句;请阅读 PEP 255,以了解yield与异常之间的相互作用的完整解释。

下面是generate_ints()生成器的使用示例:

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

You could equally write for i in generate_ints(5), or a,b,c = generate_ints(3).

在生成器函数内部,return语句只能在没有值的情况下使用,并且表示值的队列的结束;之后生成器不能返回任何进一步的值。具有诸如返回 5等值的return是生成器函数内的语法错误。生成器结果的结束也可以通过手动提高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 $国际象棋棋盘上,使得没有女王威胁另一个棋子)和骑士之旅(一个骑士到一个$ 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()函数返回找到子串“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实验室团队的其他修复。

PEP 237: Unifying Long Integers and Integers

在最近的版本中,常规整数(在大多数机器上为32位值)和长整数(其可以是任意大小)之间的区别变得令人烦恼。例如,在支持大于2**32字节的文件的平台上,文件对象的tell()方法必须返回一个长整数。然而,有各种各样的Python的字符串预期的整数,如果提供一个长整数将引发一个错误。例如,在Python 1.5中,只有普通整数可以用作切片索引,而'abc'[1L:]会引发一个TypeError索引必须是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: Changing the Division Operator

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,等等。“Floor division”是Python的/操作符当前给定的整数操作数;结果是由真正分割返回的值的底值。“经典划分”是当前混合行为/;当操作数是整数时,它返回floor除法的结果,并且当其中一个操作数是浮点数时,返回真除法的结果。

这里有变化2.2介绍:

  • 新的操作符,//是地板操作符。(是的,我们知道它看起来像C ++的注释符号。)// 始终不管其操作数的类型是什么,都执行floor划分,因此1 // t5 > 2为0且1.0 // 2.0 0.0。

    //始终可用于Python 2.2;您不需要使用__future__语句启用它。

  • By including a from __future__ import division in a module, the / operator will be changed to return the result of true division, so 1/2 is 0.5. 如果没有__future__语句,/仍然表示经典除法。除非Python 3.0,否则/的默认含义不会改变。

  • 类可以定义称为__truediv__()__floordiv__()的方法来重载两个除法运算符。在C级别,在PyNumberMethods结构中也有插槽,因此扩展类型可以定义两个运算符。

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

也可以看看

PEP 238 - 更改分区操作符
由Moshe Zadka和Guido van Rossum写。由Guido van Rossum执行

Unicode Changes

Python的Unicode支持在2.2中有所增强。Unicode字符串通常存储为UCS-2,作为16位无符号整数。Python 2.2还可以通过向configure脚本提供--enable-unicode=ucs4来编译为使用UCS-4,32位无符号整数作为其内部编码。(也可以指定--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。2.2中的8位字符串(尽管不是Unicode字符串)中添加了对称的decode([*encoding*])方法。 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__()

由Marc-AndréLemburg实施encode()decode()__unicode__()支持在内部使用UCS-4的变化由Fredrik Lundh和Martin vonLöwis实施。

也可以看看

PEP 261 - 支持“宽”Unicode字符
作者:Paul Prescod。

PEP 227: Nested Scopes

In Python 2.1, statically nested scopes were added as an optional feature, to be enabled by a from __future__ import nested_scopes directive. 在2.2嵌套的作用域中不再需要特别启用,并且现在总是存在。本节的其余部分是我的“Python 2.1新功能”文档中的嵌套作用域的描述的副本;如果你在2.1出来时阅读它,你可以跳过本节的其余部分。

Python 2.1中引入的最大的变化,在2.2中完成,是Python的范围规则。在Python 2.0中,在任何给定的时间,最多有三个命名空间用于查找变量名:local,module-level和内建命名空间。这经常令人惊讶的人,因为它不符合他们的直觉预期。例如,嵌套递归函数定义不起作用:

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

函数g()将始终引发一个NameError异常,因为名称g的绑定不在其本地命名空间或模块级命名空间。这在实践中不是一个大问题(你多长时间递归定义这样的内部函数?),但这也使用lambda语句clumsier,这在实践中是一个问题。在使用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中找到。

此更改可能会导致代码的一些兼容性问题,其中在模块级别使用相同的变量名称,而在包含更多函数定义的函数中则作为局部变量使用。这似乎不太可能,虽然,因为这样的代码本来是很容易阅读的首先。

One side effect of the change is that the from module import * and exec statements have been made illegal inside a function scope under certain conditions. Python参考手册一直说, 模块 import *法律在一个模块的顶层,但CPython解释器从来没有强制这一点。作为实现嵌套作用域的一部分,将Python源代码转换为字节码的编译器必须生成不同的代码来访问包含作用域中的变量。 模块 导入 *exec编译器不可能弄清楚这一点,因为它们在本地命名空间中添加了在编译时不可知的名称。因此,如果函数包含函数定义或包含自由变量的lambda表达式,编译器将通过引发一个SyntaxError异常来标记此事件。

为了使前面的解释更清楚,这里有一个例子:

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

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

这不应该是一个限制,因为exec在大多数Python代码中很少使用(当它被使用时,它通常是一个糟糕的设计的迹象)。

也可以看看

PEP 227 - 静态嵌套作用域
由Jeremy Hylton编写和实施。

New and Improved Modules

  • The xmlrpclib module was contributed to the standard library by Fredrik Lundh, providing support for writing XML-RPC clients. 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://www.xmlrpc.com/

  • 新的hmac模块实现由 RFC 2104描述的HMAC算法。(供稿人:GerhardHäring。)

  • 最初返回冗长元组的几个函数现在返回仍然表现为元组的伪序列,但也具有记忆属性,如memberst_mtime或tm_yearThe enhanced functions include stat(), fstat(), statvfs(), and fstatvfs() in the os module, and localtime(), gmtime(), and strptime() in the time module.

    例如,为了使用旧的元组获得文件的大小,你最终会写入像file_size = os.stat(filename) stat.ST_SIZE],但现在可以更清楚地写为file_size = os.stat .st_size

    这个功能的原始补丁是由Nick Mathewson贡献的。

  • Python分析器已经进行了大量的修改,并且其输出中的各种错误已得到纠正。(由Fred L. Drake,Jr.和Tim Peters提供。)

  • socket模块可以编译为支持IPv6;指定Python的配置脚本的--enable-ipv6选项。(供稿:Jun-ichiro“itojun”Hagino。)

  • 在支持C long long t3的平台上,对于64位整数,将struct模块添加了两个新格式字符>类型。q用于有符号64位整数,Q用于无符号整数。该值以Python的长整数类型返回。(由Tim Peters提供。)

  • 在解释器的交互模式中,有一个新的内建函数help()使用Python 2.1中引入的pydoc模块来提供交互式帮助。help(object)显示有关对象的任何可用帮助文本。help()会使您进入联机帮助实用程序,您可以在其中输入函数,类或模块的名称以阅读其帮助文本。(由Guido van Rossum撰写,使用Ka-Ping Yee的pydoc模块。)

  • 已对re模块下的SRE引擎进行了各种错误修正和性能改进。例如,在C中重写了re.sub()re.split()函数。另一个贡献的补丁将某些Unicode字符范围加速了两倍,以及一个新的finditer()方法,在给定字符串中的所有非重叠匹配上返回一个迭代器。(SRE由Fredrik Lundh维护。BIGCHARSET补丁是由Martin vonLöwis提供的。)

  • smtplib模块现在支持 RFC 2487“通过TLS的安全SMTP”,因此现在可以加密Python流程邮件传输代理被传递消息。smtplib也支持SMTP验证。(供稿人:GerhardHäring。)

  • 由Piers Lauder维护的imaplib模块支持多个新扩展: RFC 2342中定义的NAMESPACE扩展,SORT,GETACL和SETACL 。(供稿人:Anthony Baxter和Michel Pelletier。)

  • rfc822模块对电子邮件地址的解析现在符合 RFC 2822,更新为 RFC 822(模块的名称为而不是将更改为rfc2822。)还添加了一个新包,email,用于解析和生成电子邮件。(由Barry Warsaw提供,源于他在Mailman的工作。)

  • difflib模块现在包含一个新的Differ类,用于在两行文本行之间生成人类可读的变化列表(“delta”)。还有两个生成器函数,ndiff()restore(),它们分别从两个序列返回增量,或者从增量中返回原始序列之一。(由David Goodger提供的Grunt工作,来自Tim Peters的ndiff.py代码,然后进行生成)。

  • 将新常量ascii_lettersascii_lowercaseascii_uppercase添加到string模块中。标准库中有几个模块使用string.letters来表示范围A-Za-z,但是在使用语言环境时,该假设是不正确的,因为string.letters错误模块已修复,改为使用ascii_letters(由未知人士报告;由Fred L. Drake,Jr.修订)

  • mimetypes模块现在通过添加一个MimeTypes类,可以更容易地使用备选的MIME类型数据库,该类接受要解析的文件名列表。(由Fred L. Drake,Jr.提供)

  • threading模块中添加了一个Timer类,它允许调度活动在未来某个时间发生。(由Itamar Shtull-Trauring提供)

Interpreter Changes and Fixes

一些更改只影响在C级处理Python解释器的人,因为他们正在编写Python扩展模块,嵌入解释器,或者仅仅是解析器本身。如果你只写Python代码,这里描述的任何变化都不会影响你。

  • 分析和跟踪功能现在可以在C中实现,C可以以比基于Python的功能高得多的速度运行,并且应该减少分析和跟踪的开销。这将是Python开发环境的作者感兴趣的。两个新的C函数添加到Python的API,PyEval_SetProfile()PyEval_SetTrace()现有的sys.setprofile()sys.settrace()函数仍然存在,并且只是更改为使用新的C级接口。(由Fred L. Drake,Jr.提供)

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

  • 与垃圾收集器的C级接口已更改,以便更容易编写支持垃圾容器的扩展类型和调试函数的滥用。各种函数具有稍微不同的语义,因此一系列函数必须重命名。使用旧API的扩展仍将进行编译,但不会参与垃圾容器,因此为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*变量​​的指针。

  • 在方法定义表中有两个新标志METH_NOARGSMETH_O可用于简化没有参数或单个非类型化参数的方法的实现。调用这样的方法比调用使用METH_VARARGS的相应方法更有效。此外,旧的METH_OLDARGS样式的写入C方法现在已正式弃用。

  • Two new wrapper functions, PyOS_snprintf() and PyOS_vsnprintf() were added to provide cross-platform implementations for the relatively new snprintf() and vsnprintf() C lib APIs. 与标准的sprintf()vsprintf()函数相反,Python版本检查用于防止缓冲区溢出的缓冲区的边界。(供稿人:M.-A.Lemburg。)

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

Other Changes and Fixes

像往常一样,有一堆其他的改进和错误修复散布在源码树。通过CVS更改日志进行搜索,发现应用了527个修补程序,并在Python 2.1和2.2之间修复了683个错误; 2.2.1应用139个贴片和固定143个bug; 2.2.2应用106个补丁和固定的82个bug。这些数字可能是低估的。

一些更显着的变化是:

  • 用于Python的MacOS端口的代码由Jack Jansen维护,现在保存在主要的Python CVS树中,并且已进行了许多更改以支持MacOS X.

    最重要的变化是将Python构建为框架的能力,通过在编译Python时向配置脚本提供--enable-framework选项。根据Jack Jansen的说法,“这将一个自包含的Python安装加上OS X框架”粘合“安装到/Library/Frameworks/Python.framework(或其他选择的位置)。现在,这里没有什么直接的附加好处(实际上,有一个缺点,你必须改变你的PATH以能够找到Python),但它是创建一个完整的Python应用程序,移植MacPython IDE的基础,可能使用Python作为标准OSA脚本语言等等。

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

  • 传递给不支持它们的内建函数的关键字参数现在会导致引发TypeError异常,并显示消息“function没有关键字参数”。

  • 弱引用(在Python 2.1中作为扩展模块添加)现在是核心的一部分,因为它们用于实现新式类。因此,ReferenceError异常已从weakref模块中移出,成为内建异常。

  • Tim Peters的新脚本Tools/scripts/cleanfuture.py会自动从Python源代码中删除过时的__future__语句。

  • 附加的标志参数已添加到内建函数compile()中,因此__future__语句的行为现在可以在模拟shell,例如由IDLE和其他开发环境提供的那些。这在 PEP 264中有所描述。(由Michael Hudson提供。)

  • 使用Python 1.6引入的新许可证不是GPL兼容的。这是通过对2.2许可证的一些小的文本更改来修复的,所以现在合法的将Python嵌入到GPLed程序中。请注意,Python本身不是GPLed,而是一个许可证,本质上等同于BSD许可证,一如既往。许可证更改也应用于Python 2.0.1和2.1.1版本。

  • 当在Windows上显示Unicode文件名时,Python现在将其转换为MBCS编码的字符串,如Microsoft文件API所使用的。由于MBCS被文件API显式使用,Python选择ASCII作为默认编码结果是一个烦恼。在Unix上,如果locale.nl_langinfo(CODESET)可用,则使用区域设置的字符集。(Windows支持由Mark Hammond在Marc-AndréLemburg的帮助下提供。Unix支持由Martin vonLöwis添加。)

  • 现在在Windows上启用了大文件支持。(由Tim Peters提供。)

  • 现在,Tools/scripts/ftpmirror.py脚本将解析.netrc文件(如果有)。(供稿人:Mike Romberg。)

  • xrange()函数返回的对象的某些功能现已废弃,并在访问时触发警告;它们会在Python 2.3中消失。 xrange对象试图通过支持切片,序列乘法和intolist()方法和startstopstep属性也被弃用。在C级别,PyRange_New()函数的第四个参数repeat也已被弃用。

  • 字典实现有一堆补丁,主要是修复潜在的核心转储,如果一个字典包含的对象,偷偷改变了他们的哈希值,或者改变它们包含的字典。一段时间,python-dev陷入迈克尔·哈德森的温和节奏,发现一个倾销核心的案子,蒂姆·佩斯修理了这个bug,迈克尔找到了另一个案件,圆形和圆形。

  • 在Windows上,Python现在可以使用Borland C编译,这要归功于Stephen Hansen提供的一些补丁,尽管结果还不能完全正常运行。(但这进度...)

  • 另一个Windows增强功能:Wise Solutions慷慨地提供PythonLabs使用他们的InstallerMaster 8.1系统。早期的PythonLabs Windows安装程序使用Wise 5.0a,这开始显示它的年龄。(由蒂姆·彼得斯打包。)

  • .pyw结尾的文件现在可以在Windows上导入。.pyw是一个Windows操作系统,用于指示需要使用PYTHONW.EXE而不是PYTHON.EXE运行脚本,以防止DOS控制台弹出以显示输出。这个补丁使得可以导入这样的脚本,以防它们也可用作模块。(由David Bolen执行。)

  • On platforms where Python uses the C dlopen() function to load extension modules, it’s now possible to set the flags used by dlopen() using the sys.getdlopenflags() and sys.setdlopenflags() functions. (由Bram Stolk提供)

  • 当提供浮点数时,pow()内建函数不再支持3个参数。pow(x, y, z)返回/ t5> z,但是这对浮点数不会有用,最终结果根据平台不同而不可预测。pow(2.0, 8.0, 7.0)的调用现在将引入TypeError

Acknowledgements

作者要感谢以下人员为本文的各种草案提供建议,更正和协助:Fred Bremmer,Keith Briggs,Andrew Dalke,Fred L. Drake,Jr.,Carel Fellinger,David Goodger,Mark Hammond,Stephen Hansen,Michael Hudson,Jack Jansen,Marc-AndréLemburg,Martin vonLöwis,Fredrik Lundh,Michael McLay,Nick Mathewson,Paul Moore,Gustavo Niemeyer,Don O'Donnell,Joonas Paalasma,Tim Peters,Jens Quade,Tom Reinhardt,Neil Schemenauer,Guido van Rossum,Greg Ward,Edward Welbourne。