PEP 424 __length_hint __() - 有没有办法对发电机或拉链做同样的事情?

Agu*_*guy 3 python generator python-3.x python-internals

刚刚__length_hint__()从PEP 424(https://www.python.org/dev/peps/pep-0424/)遇到了这个非常棒的迭代器方法.哇!一种获取迭代器长度而不会耗尽迭代器的方法.

我的问题:

  1. 有一个简单的解释这个魔法是如何工作的?我只是好奇.
  2. 是否有限制和案例不起作用?("提示"听起来有点可疑).
  3. 有没有办法获得拉链和发电机的提示?或者它只是迭代器的基础?

编辑: BTW,我看到__length__hint__()从当前位置到结束的计数.即部分消耗的迭代器将报告剩余长度.有趣.

use*_*ica 7

哇!一种获取迭代器长度而不会耗尽迭代器的方法.

不,这是一种模糊暗示长度可能是什么的方法.没有要求它以任何方式准确.

有一个简单的解释这个魔法是如何工作的?

迭代器实现了一种__length_hint__方法,该方法使用某种特定于迭代器的信息来猜测它将输出多少元素.这个猜测可能相当不错,或者它可能非常糟糕.例如,列表迭代器知道它在列表中的位置以及列表的长度,因此它可以报告列表中剩余的元素数量.

是否有限制和案例不起作用?

如果迭代器没有足够的信息来猜测它何时会耗尽,那么它就无法实现有用的功能__length_hint__.例如,这就是发电机没有发电机的原因.无限迭代器也无法实现有用__length_hint__,因为没有办法发出无限长的信号.

有没有办法获得拉链和发电机的提示?或者它只是迭代器的基础?

zip实例和生成器都是各种迭代器.zip但是,生成器类型都不提供__length_hint__方法.


Mis*_*agi 2

这个问题有几个答案,但它们都有点没有抓住重点:这__length_hint__不是魔法。这是一个协议。如果一个对象没有实现该协议,那就是这样。


让我们绕道看看a + b,因为它是一个简单的例子。经营+者依赖a.__add__b.__radd__实际做某事。int实现__add__平均算术加法 ( 1 + 2 == 3),同时list实现__add__平均内容连接 ( [1] + [2] == [1, 2])。这是因为它__add__只是一个协议,对象如果提供它就必须遵守该协议。的定义__add__基本上只是“获取另一个操作数并返回一个对象”。

没有单独的、普遍的含义+。如果操作数不提供__add__or _radd__,则 python 无能为力。


回到实际问题,这意味着什么?

有没有简单的解释一下这个魔法是如何运作的?我只是好奇。

PEP 424中列出了所有的魔法,但它基本上是:尝试len(obj)、回退到obj.__length_hint__、使用默认值。这就是全部的魔力。

在实践中,对象必须__length_hint__根据它对自身的了解来实现。例如,采用range_iterator范围向后移植Py3.6 C 代码):

return self._stop - self._current
Run Code Online (Sandbox Code Playgroud)

在这里,迭代器知道它最多有多长,以及它提供了多少。如果它不跟踪后者,它可能仍会返回最多多长时间。无论哪种方式,它都必须使用关于自身的内部知识。

是否存在无法发挥作用的限制和情况?(“提示”听起来有点可疑)。

显然,没有实现__length_hint____len__不起作用的对象。从根本上来说,任何对其状态没有足够了解的对象都无法实现它。

链式生成器通常不实现它。例如,(a ** 2 for a in range(5))不会转发来自 的长度提示range。如果您考虑到可能存在任意的迭代器链,这是明智的: length_hint这只是预分配空间的优化,并且仅获取要放入该空间的内容可能会更快。

在其他情况下,这可能是根本不可能的。无限迭代器和随机迭代器都属于这一类,而且外部资源上的迭代器也属于这一类。

有没有办法同时获得 zip 和生成器的提示?或者它只是迭代器的基础知识?

如果一个对象没有实现__length_hint__,那么就没有。Zip 和发电机则不然,可能是出于上述效率原因。

另请注意,zip 和生成器对象是它们自己的迭代器。

foo = zip([1,2,3], [1,2,3])
id(foo) == id(iter(foo))  # returns True in py3.5
Run Code Online (Sandbox Code Playgroud)