Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 3114 – 将迭代器 .next() 重命名为迭代器.__next__()

作者:
Ka-Ping Yee <ping at zesty.ca>
状态:
最终
类型:
标准跟踪
创建:
2007-03-04
Python 版本:
3.0
历史记录:


目录

摘要

Python 2.x 中的迭代器协议包含两种方法:__iter__() 被调用在可迭代对象上以生成迭代器,和 next() 被调用在迭代器对象上以生成序列中的下一个项目。使用 for 循环迭代可迭代对象会隐式调用这两种方法。本 PEP 建议将 next 方法重命名为 __next__,与 Python 中所有其他协议一致,这些协议中方法在语言级协议的一部分中被隐式调用,并且引入一个名为 next 的内置函数来调用 __next__ 方法,与显式调用其他协议的方式一致。

双下划线名称

在 Python 中,名称前后的双下划线用于区分属于语言本身的名称。解释器隐式使用或创建的属性和方法使用此命名约定;一些示例包括

  • __file__ - 解释器自动创建的属性
  • __dict__ - 对解释器具有特殊意义的属性
  • __init__ - 解释器隐式调用的方法

请注意,此约定适用于由程序员显式定义的 __init__ 之类的方法,以及只能通过显式命名访问的 __file__ 之类的属性,因此它包括由解释器使用创建的名称。

(并非所有被称为“协议”的东西都是由带有双下划线名称的方法组成的。例如,__contains__ 方法有双下划线,因为语言结构 x in y 隐式调用 __contains__。但是,即使 read 方法是文件协议的一部分,它也没有双下划线,因为没有语言结构会隐式调用 x.read()。)

双下划线的使用为作为 Python 语言定义一部分的名称创建了一个单独的命名空间,因此程序员可以自由创建以字母开头的变量、属性和方法,而不必担心与具有语言定义目的的名称发生静默冲突。(与保留关键字发生冲突仍然是一个问题,但至少这会立即导致语法错误。)

迭代器上的 next 方法的命名是此约定的例外。代码中如果没有任何地方包含对 next 方法的显式调用,仍然会受到此类方法的存在的静默影响。因此,本 PEP 建议迭代器应该具有 __next__ 方法而不是 next 方法(语义不变)。

双下划线方法和内置函数

Python 语言定义了几个协议,这些协议是通过定义带有双下划线名称的方法来实现或自定义的。在每种情况下,协议都由解释器中作为 C 函数实现的内部方法提供。对于在 Python 中定义的对象,此 C 函数通过隐式调用具有双下划线名称的 Python 方法来支持自定义(它通常在仅仅调用 Python 方法之外还做一些额外的工作)。

有时协议由语法结构调用

  • x[y] –> internal tp_getitem –> x.__getitem__(y)
  • x + y –> internal nb_add –> x.__add__(y)
  • -x –> internal nb_negative –> x.__neg__()

有时没有语法结构,但能够显式调用协议仍然很有用。对于这种情况,Python 提供了一个与之同名的内置函数,但没有双下划线。

  • len(x) –> internal sq_length –> x.__len__()
  • hash(x) –> internal tp_hash –> x.__hash__()
  • iter(x) –> internal tp_iter –> x.__iter__()

遵循此模式,处理 next 的自然方法是添加一个 next 内置函数,其行为与之完全相同。

  • next(x) –> internal tp_iternext –> x.__next__()

此外,建议 next 内置函数接受哨兵值作为可选的第二个参数,遵循 getattriter 内置函数的风格。当用两个参数调用时,next 会捕获 StopIteration 异常,并返回哨兵值而不是传播异常。这在 iternext 之间创造了一个很好的对偶性

iter(function, sentinel) <–> next(iterator, sentinel)

之前的提案

此提议并非一个新想法。这里提出的想法得到了 BDFL 在 python-dev 上的支持 [1] 甚至在最初的迭代器 PEP 中也有提到,PEP 234

(In retrospect, it might have been better to go for __next__()
and have a new built-in, next(it), which calls it.__next__().
But alas, it's too late; this has been deployed in Python 2.2
since December 2001.)

反对意见

对添加更多内置函数有一些反对意见。特别是,Martin von Loewis 写道 [2]

I dislike the introduction of more builtins unless they have a true
generality (i.e. are likely to be needed in many programs). For this
one, I think the normal usage of __next__ will be with a for loop, so
I don't think one would often need an explicit next() invocation.

It is also not true that most protocols are explicitly invoked through
builtin functions. Instead, most protocols are can be explicitly invoked
through methods in the operator module. So following tradition, it
should be operator.next.

...

As an alternative, I propose that object grows a .next() method,
which calls __next__ by default.

过渡计划

2to3 翻译工具将添加两个额外的转换 [3]

  • 命名为 next 的方法定义将被重命名为 __next__
  • next 方法的显式调用将被替换为对内置 next 函数的调用。例如,x.next() 将变为 next(x)

Collin Winter 研究了根据模块级绑定到 next 的存在自动决定是否执行第二个转换的可能性 [4] 并发现这将是“丑陋且缓慢的”。相反,翻译工具将在检测到这种绑定时发出警告。Collin 针对以下情况提出了警告 [5]

  • next 的模块级赋值。
  • 模块级定义名为 next 的函数。
  • 模块级导入名称 next
  • __builtin__.next 的赋值。

批准

此 PEP 于 2007 年 3 月 6 日由 Guido 接受 [6].

实施

Georg Brandl 编写了一个包含必要更改的补丁(除了 2to3 工具),并将其作为修订版 54910 提交。

参考文献


来源: https://github.com/python/peps/blob/main/peps/pep-3114.rst

最后修改时间: 2023-09-09 17:39:29 GMT