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

Python 增强提案

PEP 3114 – 将 iterator.next() 重命名为 iterator.__next__()

作者:
Ka-Ping Yee <ping at zesty.ca>
状态:
最终版
类型:
标准跟踪
创建日期:
2007年3月4日
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] –> 内部 tp_getitem –> x.__getitem__(y)
  • x + y –> 内部 nb_add –> x.__add__(y)
  • -x –> 内部 nb_negative –> x.__neg__()

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

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

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

  • next(x) –> 内部 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]

实施

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

参考资料


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

最后修改: 2025-02-01 08:55:40 GMT