Python 2.2 新增Function

  • Author

    • A.M. Kuchling

Introduction

本文介绍了 2002 年 10 月 14 日发布的 Python 2.2.2 的新Function。Python 2.2.2 是 Python 2.2 的错误修正版本,最初于 2001 年 12 月 21 日发布。

可以将 Python 2.2 视为“清理版本”。有些Function(例如生成器和迭代器)是全新的,但是大多数更改(尽管可能是有意义的且影响深远的)旨在清除语言设计中的不规则之处和阴暗的角落。

本文并不试图提供新Function的完整规范,而是提供了一个方便的概述。有关完整的详细信息,您应该参考 Python 2.2 的文档,例如Python 库参考Python 参考手册。如果您想了解更改的完整实现和设计原理,请参考 PEP 以获得特定的新Function。

PEP 252 和 253:类型和类别更改

Python 2.2 中最大和最深远的变化是对 Python 的对象和类的模型。所做的更改应向后兼容,因此您的代码可能会 continue 保持不变运行,但所做的更改提供了一些惊人的新Function。在开始本文的最长时间和最复杂的部分之前,我将概述这些更改并提供一些 Comment。

很久以前,我写了一个网页,列出了 Python 设计中的缺陷。最重大的缺陷之一是无法对用 C 实现的 Python 类型进行子类化。特别是,无法对内置类型进行子类化,因此,您不能仅对列表进行子类化以添加单个有用的方法。给他们。 UserList模块提供了一个类,该类支持列表的所有方法,并且可以进一步子类化,但是有许多 C 代码期望使用常规的 Python 列表,并且不接受UserList实例。

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

  • 您可以对内置类型进行子类化,例如列表甚至整数,并且您的子类应在需要原始类型的所有位置工作。

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

  • 也可以使用称为* properties *的新机制在访问或设置实例属性时自动调用方法。可以将getattr()的许多用法 Rewrite 为使用属性,从而使生成的代码更简单,更快捷。作为一个小的附带好处,属性现在也可以具有文档字符串。

  • 可以使用* slots *将实例的合法属性列表限制为特定的集合,从而可以防止 Importing 错误,并可能在将来的 Python 版本中进行更多优化。

一些用户已经对所有这些更改表示了担忧。他们说,可以肯定的是,这些新Function很简洁,可以进行各种各种技巧,这些技巧在以前的 Python 版本中是不可能的,但是它们也使语言更加复杂。有人说他们一直推荐 Python 的简单性,并觉得它的简单性正在丢失。

就个人而言,我认为无需担心。许多新Function都非常深奥,您可以编写很多 Python 代码,而无需意识到它们。编写简单的类比以往任何时候都没有困难,因此除非 true 需要它们,否则您无需费心学习或教授它们。以前只有 C 才能完成的一些非常复杂的任务现在可以在纯 Python 中实现,在我看来,这一切都变得更好。

我不会try涵盖使新Function正常工作所需的所有单个案例和小的更改。取而代之的是,本节将仅画出大致的笔画。有关 Python 2.2 的新对象模型的更多信息源,请参见Related Links的“相关链接”。

新旧类

首先,您应该知道 Python 2.2 实际上有两种类:经典或旧式类,以及新式类。旧样式的类模型与 Python 早期版本中的类模型完全相同。本节中描述的所有新Function仅适用于new-style类。这种分歧并非永远存在。finally,可能会在 Python 3.0 中删除旧式类。

那么如何定义一个新的类呢?您可以pass对现有的新样式类进行子类化来实现。 Python 的大多数内置类型,例如整数,列表,字典,甚至文件,现在都是new-style类。还添加了一个名为object的新类,该类是所有内置类型的 Base Class,因此,如果没有合适的内置类型,则可以仅继承object

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

这意味着没有任何 Base Class 的class语句在 Python 2.2 中始终是经典类。 (实际上,您也可以pass设置名为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()钩子时,这仍然可能不准确。

新类模型的一个主要思想是,用于使用* descriptors *描述对象属性的 API 已经形式化。Descriptors 指定属性的值,说明它是方法还是字段。使用 DescriptorsAPI,可以使用静态方法和类方法,以及更特殊的构造。

属性 Descriptors 是存在于类对象中的对象,它们具有一些自己的属性:

  • name是属性的名称。

  • __doc__是属性的文档字符串。

  • __get__(object)是一种从* object *检索属性值的方法。

  • __set__(object, value)将* object 上的属性设置为 value *。

  • __delete__(object, value)删除* object value *属性。

例如,当您编写obj.x时,Python 实际执行的步骤是:

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

对于方法,descriptor.__get__()返回可调用的临时对象,并包装实例和要在其上调用的方法。这也是为什么现在可以使用静态方法和类方法的原因。它们具有仅包装方法,方法或类的 Descriptors。作为对这些新方法的简要说明,静态方法未pass实例传递,因此类似于常规函数。类方法传递给对象的类,而不传递对象本身。静态和类方法的定义如下:

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

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

staticmethod()函数采用函数f(),并将其包装在 Descriptors 中返回,以便可以将其存储在类对象中。您可能会期望有特殊的语法来创建此类方法(def static fdefstatic f()或类似的名称),但尚未定义此类语法;留给将来的 Python 版本。

更多的新Function(例如插槽和属性)也被实现为new-style的 Descriptors,编写具有新颖性的 Descriptors 类并不难。例如,有可能编写一个 Descriptors 类,该类使编写方法的埃菲尔风格前置条件和后置条件成为可能。使用此Function的类可以这样定义:

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()的人不必了解有关 Descriptors 的任何信息。这就是为什么我认为新Function不会增加语言的基本复杂性的原因。为了编写eiffelmethod()或 ZODB 或其他内容,会有一些向导需要了解它,但是大多数用户只会在生成的库之上编写代码,而忽略实现细节。

多重继承:钻石规则

pass更改解析名称的规则,多重继承也变得更加有用。考虑以下类集(图由 Guido van Rossum 摘自 PEP 253):

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

经典类的查找规则很简单,但不是很聪明。从左到右,深度优先搜索 Base Class。对D.save()的引用将搜索类别DB,然后搜索A,其中将找到并返回save()C.save()根本找不到。这很糟糕,因为如果Csave()方法保存了C特定的某些内部状态,则不调用它将导致该状态永远不会被保存。

new-style类遵循不同的算法,解释起来有点复杂,但是在这种情况下做正确的事情。 (请注意,Python 2.3 将此算法更改为在大多数情况下会产生相同结果的算法,但对于 true 复杂的继承图却会产生更有用的结果.)

  • 遵循经典的查找规则,列出所有 Base Class,如果重复访问该类,则多次包含该类。在上面的示例中,访问类别的列表为[+86++87++88++89++90+]。

  • 扫描列表以查找重复的类。如果找到任何东西,则除去所有出现的事件,将* last *留在列表中。在上面的示例中,删除重复项后,列表变为[+91++92++93++94+]。

遵循此规则,引用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()定义了用于属性访问的钩子;最常见的做法是为了方便起见,pass自动将诸如obj.parent之类的属性访问 Map 到诸如obj.get_parent之类的方法调用中,使代码更具可读性。 Python 2.2 添加了一些控制属性访问的新方法。

首先,new-style类仍支持__getattr__(attr_name),并且没有任何改变。和以前一样,当try访问obj.foo并且在实例的字典中未找到名为foo的属性时,将调用该方法。

new-style类也支持新方法__getattribute__(attr_name)。两种方法之间的区别在于,只要访问任何属性,“总是”便会调用getattribute(),而仅当实例的字典中找不到foo时才调用旧的getattr()

但是,Python 2.2 对* properties *的支持通常是捕获属性引用的更简单方法。编写getattr()方法很复杂,因为要避免递归,您不能在其中使用常规的属性访问,而必须弄乱dict的内容。 getattr()方法finally也会在检查其他方法(例如repr()coerce())时被 Python 调用,因此必须牢记这一点。最后,在每个属性访问上调用一个函数会导致相当大的性能损失。

property是一种新的内置类型,它打包了三个用于获取,设置或删除属性的函数以及一个文档字符串。例如,如果要定义一个已计算但也可设置的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")

这肯定比Pairgetattr()/setattr()方法更清晰,更容易编写,这两个方法检查size属性并在从实例dict检索所有其他属性的同时专门处理它。对size的访问也是唯一必须执行调用函数的工作,因此对其他属性的引用以其通常的速度运行。

最后,可以使用新的__slots__ class 属性约束可以在对象上引用的属性列表。 Python 对象通常是动态的。在任何时候,只需执行obj.new_attr=1即可在实例上定义新属性。new-style类可以定义名为__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'

请注意,try分配给__slots__中未列出的属性时,您会得到AttributeError的信息。

本节只是对新Function的快速概述,提供了足够的说明来开始进行编程,但是许多细节已被简化或忽略。您应该去哪里获得更完整的图片?

https://docs.python.org/dev/howto/descriptor.html是由 Guido van Rossum 撰写的关于 Descriptors Function的冗长教程介绍。如果我的描述吸引了您的胃口,请 continue 阅读本教程,因为它在不改变原有Function的情况下对新Function进行了更详细的介绍。

接下来,有两个相关的 PEP, PEP 252 PEP 253 PEP 252的标题为“使类型看起来更像类”,并涵盖了 DescriptorsAPI。 PEP 253的标题为“子类型化内置类型”,并描述了对类型对象的更改,这些更改使得可以对内置对象进行子类型化。 PEP 253是两者中比较复杂的 PEP,在某些时候,关于类型和元类型的必要解释可能会导致您的头部爆炸。这两个 PEP 均由 Guido van Rossum 编写和实施,并获得了 Zope Corp.团队其他成员的大力协助。

PEP 234:迭代器

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 的迭代器,直到它返回 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插槽的序列自动构造迭代器,因此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
    ...

注意,您只能在迭代器中前进;无法获取上一个元素,重置迭代器或对其进行复制。迭代器对象可以提供此类附加Function,但是迭代器协议仅需要next()方法。

See also

  • PEP 234-迭代器

  • 由 Ka-Ping Yee 和 GvR 撰写;由 Python Labs 小组(主要由 GvR 和 Tim Peters)实施。

PEP 255:简单生成器

生成器是另一个新Function,它与迭代器的引入进行交互。

您毫无疑问地熟悉函数调用在 Python 或 C 中的工作方式。当您调用函数时,它会获得一个私有的命名空间,用于创建其局部变量。当函数到达return语句时,局部变量将被销毁并将结果值返回给调用方。以后调用同一函数将获得一组新的局部变量。但是,如果在退出函数时没有扔掉局部变量怎么办?如果您以后可以在break的地方 continue 运行该怎么办?这就是 Generator 提供的;可以将它们视为可恢复的Function。

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

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

为生成器引入了一个新关键字yield。任何包含yield语句的函数都是生成器函数;这是由 Python 的字节码编译器检测到的,该编译器因此专门编译了该函数。由于引入了新关键字,因此必须pass在模块源代码顶部附近包含from __future__ import generators语句,在模块中显式启用生成器。在 Python 2.3 中,此语句将变得不必要。

调用生成器函数时,它不会返回单个值;而是返回一个支持迭代器协议的生成器对象。执行yield语句后,生成器将输出i的值,类似于return语句。 yieldreturn语句之间的最大区别在于,到达yield时,生成器的执行状态被挂起,并且保留了局部变量。在下一次调用生成器的next()方法时,该函数将在yield语句之后立即恢复执行。 (由于复杂的原因,在tryfinally语句的try块中不允许使用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

您可以同样地写for i in generate_ints(5)a,b,c = generate_ints(3)

在生成器函数内部,return语句只能在不带值的情况下使用,并指示值的处理结束。之后,生成器无法返回任何其他值。具有值return 5return是生成器函数内部的语法错误。生成器结果的结尾也可以pass手动提高StopIteration或仅让执行流程脱离函数的底部来指示。

pass编写自己的类并将生成器的所有局部变量存储为实例变量,可以手动实现生成器的效果。例如,返回整数列表可以pass将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 皇后问题提供了解决方案(将$ N $皇后放置在$ NxN $棋盘上,这样就没有女王可以威胁另一个皇后)和 Knight's Tour(骑士之旅),该 Route 需要骑士到$$的每个平方 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 界面的一个新颖 Feature 是生成器的状态表示为具体对象(迭代器),可以将其传递给其他函数或存储在数据结构中。

See also

  • PEP 255-简单生成器

  • 由 Neil Schemenauer,Tim Peters 和 Magnus Lie Hetland 撰写。主要由 Neil Schemenauer 和 Tim Peters 实施,以及 Python Labs 小组的其他修复程序。

PEP 237:统一长整数和整数

在最新版本中,正则整数(在大多数计算机上是 32 位值)和长整数(可以是任意大小)之间的区别变得令人讨厌。例如,在支持大于2**32个字节的文件的平台上,文件对象的tell()方法必须返回一个长整数。但是,Python 的各个位都期望使用纯整数,如果提供了一个长整数,则会引发错误。例如,在 Python 1.5 中,只能将常规整数用作切片索引,并且'abc'[1L:]会引发TypeError异常,并显示消息“切片索引必须为 int”。

Python 2.2 将根据需要将值从短整数转换为长整数。不再需要'L'后缀来指示长整数 Literals,因为现在编译器将选择适当的类型。 (在以后的 Python 2.x 版本中不建议使用'L'后缀,在 Python 2.4 中触发警告,并可能在 Python 3.0 中删除.)许多用于引发OverflowError的操作现在都将返回一个长整数作为其整数。结果。例如:

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

在大多数情况下,整数和长整数现在将被相同地对待。您仍然可以使用type()内置函数来区分它们,但这很少需要。

See also

  • 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 是动态类型的,因此可能难以确定操作数的可能类型。

(争议在于是否真的是一个设计缺陷,是否值得 break 现有的代码来解决这个问题.这引起了关于 python-dev 的无休止的讨论,并于 2001 年 7 月爆发成在* comp 上的 ly 讽讽刺风暴.lang.python *.在这里我不会 Arguments 任何一方,并且会坚持描述 2.2 中的实现.请阅读 PEP 238以获取有关参数和反参数的摘要。)

由于此更改可能会破坏代码,因此正在逐步引入。 Python 2.2 开始了过渡,但是切换要到 Python 3.0 才能完成。

首先,我将从 PEP 238借用一些术语。 “true 的除法”是大多数非程序员熟悉的除法:3/2 为 1.5,1/4 为 0.25,依此类推。给定整数操作数时,“地板除法”是 Python 的/运算符当前执行的操作;结果是真实除法返回的值的下限。 “经典除法”是/的当前混合行为;当操作数是整数时,它返回下限除法的结果;当操作数之一是浮点数时,它返回真除法的结果。

以下是 2.2 引入的更改:

  • 新的运算符//是楼层划分运算符。 (是的,我们知道它看起来像 C 的 Comments 符号.)// 始终执行地板分割,无论其操作数的类型如何,因此1 // 2为 0 且1.0 // 2.0也为 0.0.

//在 Python 2.2 中始终可用;您无需使用__future__语句启用它。

  • pass在模块中包含from __future__ import division,将更改/运算符以返回真除法的结果,因此1/2为 0.5. 没有__future__语句,/仍表示经典除法。 /的默认含义在 Python 3.0 之前不会更改。

  • 类可以定义名为truediv()floordiv()的方法以重载两个除法运算符。在 C 级别,PyNumberMethods结构中还包含一些插槽,因此扩展类型可以定义两个运算符。

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

See also

  • PEP 238-更改除法运算符

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

Unicode Changes

Python 的 Unicode 支持在 2.2 中有所增强。 Unicode 字符串通常以 16 位无符号整数的形式存储为 UCS-2.pass将--enable-unicode=ucs4提供给 configure 脚本,还可以将 Python 2.2 编译为使用 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 中,对称decode([*encoding*])方法已添加到 8 位字符串(尽管不是 Unicode 字符串)中。 decode()假定字符串采用指定的编码并对其进行解码,并返回编解码器返回的内容。

使用此新Function,为与 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 实现。 Fredrik Lundh 和 Martin vonLöwis 对内部使用 UCS-4 的支持进行了更改。

See also

  • PEP 261-支持“宽” Unicode 字符

  • 保罗·普雷斯科德(Paul Prescod)撰写。

PEP 227:嵌套合并范围

在 Python 2.1 中,添加了静态嵌套作用域作为可选Function,以passfrom __future__ import nested_scopes指令启用。在 2.2 版中,不再需要特别启用嵌套作用域,并且现在一直存在。本节的其余部分是“我的 Python 2.1 新增Function”文档中嵌套作用域说明的副本。如果您在 2.1 发布时阅读了该文档,则可以跳过本节的其余部分。

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

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

函数g()将始终引发NameError异常,因为名称g的绑定既不在其本地名称空间中也不在模块级名称空间中。在实践中,这并不是什么大问题(您多久递归地定义这种内部函数?”,但这也是使用lambda语句变得更加笨拙的,这在实践中是一个问题。在使用lambda的代码中,通常可以pass将它们传递为参数的默认值来查找要复制的局部变量。

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

结果,以强大的Function风格编写的 Python 代码的可读性受到很大影响。

Python 2.2 的最重要更改是该语言已添加了静态作用域以解决此问题。首先,上面的示例中现在不需要name=name默认参数。简而言之,当给定的变量名称未在函数中分配值时(pass赋值,或defclassimport语句),将在封闭范围的本地名称空间中查找对该变量的引用。在 PEP 中可以找到有关规则的更详细说明以及实现的剖析。

此更改可能会导致代码的某些兼容性问题,在该代码中,在模块级别和用作包含更多函数定义的函数中的局部变量都使用相同的变量名。不过,这似乎不太可能,因为这样的代码最初阅读起来会很混乱。

更改的一个副作用是,在某些情况下,from module import *exec语句在函数范围内已变为非法。 Python 参考手册一直说from module import *仅在模块的顶层是合法的,但是 CPython 解释器以前从未执行过此操作。作为实现嵌套作用域的一部分,将 Python 源代码转换为字节码的编译器必须生成不同的代码以访问包含作用域中的变量。 from module import *exec使编译器无法弄清楚这一点,因为它们将名称添加到了在编译时无法得知的本地名称空间。因此,如果函数包含带有自由变量的函数定义或lambda表达式,则编译器将pass引发SyntaxError异常来进行标记。

为了使前面的解释更加清楚,下面是一个示例:

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

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

这应该不是很大的限制,因为在大多数 Python 代码中很少使用exec(并且当使用exec时,无论如何它通常都是不良设计的标志)。

See also

  • PEP 227-静态嵌套范围

  • 由 Jeremy Hylton 编写并实施。

新模块和改进模块

  • xmlrpclib模块由 Fredrik Lundh 贡献给标准库,它提供了编写 XML-RPCClient 端的支持。 XML-RPC 是构建在 HTTP 和 XML 之上的简单的远程过程调用协议。例如,以下代码段从 O'Reilly 网络检索 RSSChannels 列表,然后列出一个 Channels 的最新标题:
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 提供.)

  • 最初返回冗长的 Tuples 的几个函数现在返回伪序列,这些伪序列的行为仍然类似于 Tuples,但具有助记符属性,例如 memberst_mtime 或tm_year。增强Function包括os模块中的stat()fstat()statvfs()fstatvfs(),以及time模块中的localtime()gmtime()strptime()

例如,要使用旧的 Tuples 获取文件的大小,您将finally编写类似file_size = os.stat(filename)[stat.ST_SIZE]的东西,但是现在可以更清晰地写为file_size = os.stat(filename).st_size

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

  • Python 探查器已经过大量返工,其输出中的各种错误也已得到纠正。 (由 Fred L. Drake,Jr.和 Tim Peters 贡献.)

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

  • 在支持 C long long类型的平台上,将两个新的格式字符添加到struct模块中,以获取 64 位整数。 q是有符号的 64 位整数,Q是无符号的整数。该值以 Python 的长整数类型返回。 (由蒂姆·彼得斯贡献.)

  • 在解释器的交互模式下,有一个新的内置函数help(),该函数使用 Python 2.1 中引入的pydoc模块提供交互帮助。 help(object)显示有关* object *的所有可用帮助文本。 help()(不带参数)将使您进入在线帮助 Util,您可以在其中 Importing 函数,类或模块的名称以读取其帮助文本。 (由 Guido van Rossum 使用 Ka-Ping Yee 的pydoc模块提供。)

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

  • smtplib模块现在支持 RFC 2487,即“pass TLS 保护 SMTP”,因此现在可以对 Python 程序与传递消息的邮件传输代理之间的 SMTP 通信进行加密。 smtplib还支持 SMTP 身份验证。 (由 GerhardHäring 提供.)

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

  • rfc822模块的电子邮件地址解析现在符合 RFC 2822(对 RFC 822的更新)。 (该模块的名称为* not *,将不会更改为rfc2822.)还添加了一个新包email,用于解析和生成电子邮件。 (由 Barry Warsaw 贡献,并且源于他在 Mailman 上的工作.)

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

  • 新的常量ascii_lettersascii_lowercaseascii_uppercase已添加到string模块。标准库中有几个模块使用string.letters表示范围 A-Za-z,但是这种假设在使用语言环境时是不正确的,因为string.letters随当前语言环境定义的合法字符集的不同而不同。越野车模块已全部固定为使用ascii_letters代替。 (由不知名人士举报;由小弗雷德·德雷克(Fred L. Drake,Jr.)修复。

  • 现在,pass添加MimeTypes类,该mimetypes模块使使用备用 MIME 类型的数据库更加容易,该类接受要解析的文件名列表。 (由小弗雷德·L·德雷克(Fred L. Drake,Jr.)提供。

  • Timer类已添加到threading模块,该类允许安排活动在将来的某个时间发生。 (由 Itamar Shtull-Trauring 贡献.)

Interpreter 的更改和修正

其中一些更改仅影响在 C 级别与 Python 解释器打交道的人员,因为他们正在编写 Python 扩展模块,嵌入解释器或只是对解释器本身进行黑客攻击。如果仅编写 Python 代码,则此处描述的任何更改都不会对您造成太大影响。

  • 现在可以在 C 语言中实现概要分析和跟踪Function,其运行速度比基于 Python 的函数要高得多,并应减少概要分析和跟踪的开销。 Python 开发环境的作者对此将很感兴趣。 Python 的 API 中添加了两个新的 C 函数PyEval_SetProfile()PyEval_SetTrace()。现有的sys.setprofile()sys.settrace()函数仍然存在,并且已被简单更改为使用新的 C 级接口。 (由小弗雷德·L·德雷克(Fred L. Drake,Jr.)提供。

  • 添加了另一个低级 API,主要是 Python 调试器和开发工具的实现者感兴趣的。 PyInterpreterState_Head()PyInterpreterState_Next()让呼叫者浏览所有现有的解释器对象; PyInterpreterState_ThreadHead()PyThreadState_Next()允许循环访问给定解释器的所有线程状态。 (由 David Beazley 贡献.)

  • 更改了垃圾收集器的 C 级接口,以使其更容易编写支持垃圾收集的扩展类型并调试Function的滥用。各种Function的语义略有不同,因此必须重命名一堆Function。使用旧 API 的扩展仍会编译,但不会参与垃圾回收,因此将它们更新为 2.2 应该被认为是相当高的优先级。

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

  • Py_TPFLAGS_GC()重命名为PyTPFLAGS_HAVE_GC()

  • 从对象大小计算中删除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 方法。

  • 添加了两个新的包装器函数PyOS_snprintf()PyOS_vsnprintf(),以提供相对较新的snprintf()vsnprintf() C lib API 的跨平台实现。与标准sprintf()vsprintf()函数相反,Python 版本检查用于防止缓冲区溢出的缓冲区范围。 (由 M.-A.Lemburg 提供)

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

其他更改和修复

像往常一样,在源代码树中还分散了许多其他改进和错误修正。在 CVS 变更日志中进行搜索后发现,在 Python 2.1 和 2.2 之间修复了 527 个补丁,并修复了 683 个错误。 2.2.1 应用了 139 个补丁并修复了 143 个错误; 2.2.2 应用了 106 个补丁并修复了 82 个错误。这些数字可能被低估了。

一些更显着的变化是:

  • Jack Jansen 维护的 Python 的 MacOS 端口代码现在保留在 Python CVS 主树中,并且进行了许多更改以支持 MacOSX。

最重大的变化是将 Python 构建为框架的能力,该Functionpass在编译 Python 时向 configure 脚本提供--enable-framework选项来启用。杰克·詹森(Jack Jansen)表示:“这会将自包含的 Python 安装以及 OS X 框架“胶水”安装到/Library/Frameworks/Python.framework(或其他选择的位置)中。目前,这几乎没有立即带来的好处(实际上,缺点是您必须更改 PATH 才能找到 Python),但这是创建Function强大的 Python 应用程序,移植 MacPython IDE 的基础,并可能使用 Python 作为标准的 OSA 脚本语言等等。

与 MacOS API 交互的大多数 MacPython 工具箱模块(例如窗口,QuickTime,脚本等)已移植到 OS X,但在setup.py中已被 Comments 掉。想要try这些模块的人可以手动取消 Comments。

  • 传递给不带内置函数的关键字参数现在会引发TypeError异常,并显示消息“ * function *不带关键字参数”。

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

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

  • 内置函数compile()中添加了一个附加的* flags 参数,因此现在可以在模拟 shell(例如由 IDLE 和其他开发环境提供的 shell)中正确观察到__future__语句的行为。 PEP 264中对此进行了描述。 (由 Michael Hudson 提供.)

  • Python 1.6 引入的新许可证与 GPL 不兼容。pass对 2.2 许可证进行一些小的 Literals 更改即可解决此问题,因此现在再次将 Python 嵌入 GPLed 程序中是合法的。请注意,Python 本身不是经过 GPL 处理的,而是获得了与以前一直相同的实质上等同于 BSD 许可的许可。许可证更改还应用于 Python 2.0.1 和 2.1.1 版本。

  • 在 Windows 上以 Unicode 文件名呈现时,Python 现在会将其转换为 Microsoft 文件 API 所使用的 MBCS 编码的字符串。由于文件 API 明确使用了 MBCS,因此 Python 选择 ASCII 作为默认编码变得很烦人。在 Unix 上,如果locale.nl_langinfo(CODESET)可用,则使用语言环境的字符集。 (Windows 支持由 Mark Hammond 在 Marc-AndréLemburg 的帮助下提供.Unix 支持由 Martin vonLöwis 添加.)

  • 现在在 Windows 上启用了大文件支持。 (由蒂姆·彼得斯贡献.)

  • Tools/scripts/ftpmirror.py脚本现在可以解析.netrc文件(如果有)。 (由 Mike Romberg 提供.)

  • 现在已弃用xrange()函数返回的对象的某些Function,并在访问它们时触发警告。它们将在 Python 2.3 中消失。 xrange对象pass支持切片,序列乘法和in运算符来 Feign 它们是完整的序列类型,但是这些Function很少使用,因此存在故障。 tolist()方法以及startstopstep属性也已被弃用。在 C 级别,PyRange_New()函数的第四个参数repeat也已被弃用。

  • 字典实现有很多补丁,主要用于修复潜在的核心转储,如果字典中包含的对象悄悄地更改了其哈希值,或对其所包含的字典进行了突变。一段时间以来,python-dev 陷入了一种缓慢的节奏迈克尔·哈德森(Michael Hudson)找到了一个遗弃核心的案例,蒂姆·彼得斯(Tim Peters)修复了该错误,迈克尔(Michael)找到了另一个案例,然后又绕了过去。

  • 在 Windows 上,由于 Stephen Hansen 提供了许多补丁,现在可以使用 Borland C 编译 Python,尽管结果还不能完全发挥作用。 (但这是*进展中...)

  • Windows 的另一项增强Function:Wise Solutions 慷慨地为 PythonLabs 提供了 InstallerMaster 8.1 系统的使用。早期的 PythonLabs Windows 安装程序使用的是 Wise 5.0a,这已经开始显示其年龄。 (由蒂姆·彼得斯包装.)

  • 现在可以在 Windows 上导入以.pyw结尾的文件。 .pyw是仅 Windows 的内容,用于指示需要使用 PYTHONW.EXE 而不是 PYTHON.EXE 运行脚本,以防止 DOS 控制台弹出来显示输出。如果这些脚本也可用作模块,此补丁程序可以导入这些脚本。 (由 David Bolen 实施)。

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

  • 提供浮点数时,pow()内置函数不再支持 3 个参数。 pow(x, y, z)返回(x**y) % z,但这对于浮点数从没有用,并且finally结果根据平台的不同而变化。诸如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。