【问题标题】:Why Generators are exhaustive and Lists/Tuples are not? [duplicate]为什么生成器是详尽的而列表/元组不是? [复制]
【发布时间】:2018-09-15 18:46:11
【问题描述】:

首先我不得不说我在阅读这篇文章之前阅读了很多 SO 帖子,因为我找不到我要找的东西,或者我可能不明白。 就这样吧

我有点理解 Iterables 和 Iterators 是什么。因此,任何包含可以迭代的项目的容器对象(如 Lists/Tuples/Sets)都称为 Iterables。现在要遍历 Iterables,您需要 Iterators,它发生的方式是因为 __iter__ 方法为您提供类型的 Iterator 对象,然后在 Iterator 对象上调用 __next__ 以提取值。

因此,要使任何对象可迭代,您需要定义 iternext 方法,我认为列表也是如此。但是我最近发现的奇怪的部分来了。

l1 = [1,2,3]
hasattr(l1, "__next__")
Out[42]: False
g = (x for x in range(3))
hasattr(g, "__next__")
Out[44]: True

现在由于列表确实支持迭代器协议,为什么在它们的实现中缺少 __next__ 方法,如果确实缺少,那么列表的迭代如何工作?

list_iterator = iter(l1)
next(list_iterator)
Out[46]: 1
next(list_iterator)
Out[47]: 2
next(list_iterator)
Out[48]: 3
next(list_iterator)
Traceback (most recent call last):
  File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-49-56e733bbb896>", line 1, in <module>
    next(list_iterator)
StopIteration

gen0_iterator = iter(g)
gen_iterator = iter(g)
next(gen_iterator)
Out[57]: 0
next(gen_iterator)
Out[58]: 1
next(gen_iterator)
Out[59]: 2
next(gen_iterator)
Traceback (most recent call last):
  File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-60-83622dd5d1b9>", line 1, in <module>
    next(gen_iterator)
StopIteration
gen_iterator1 = iter(g)
next(gen_iterator1)
Traceback (most recent call last):
  File "C:\Users\RJ\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-62-86f9b3cc341f>", line 1, in <module>
    next(gen_iterator1)
StopIteration

我为列表创建了一个迭代器,然后在其上调用 next 方法来获取元素并且它可以工作。

  1. 现在,如果之前的 hasattr(a, "__next__") 返回 False,那么我们如何调用迭代器对象的 next 方法来获取列表。

  2. 现在让我想到这一切的原始问题,无论我迭代列表多少次,它都不会耗尽,并且每次调用 iter() 都会返回一个新的迭代器对象,但以防万一生成器不会发生这种情况,一旦生成器耗尽,无论您调用多少次iter(),它总是会给您返回已经引发StopIteration 异常的相同对象,这又是真的,因为迭代器一旦提出StopIteration,它总是会,但为什么它不会发生在列表中。

此外,这与 python 文档对 conatiner.__ iter__ 所说的一致,container.__iter__ 为您提供类型的迭代器对象,iterator.__ iter__iterator.__iter__ 为您提供迭代器对象本身,这正是原因在生成器上调用iter() 一遍又一遍地返回相同的对象。但为什么,更重要的是如何?

这里还有一点需要注意的是

isinstance(l1 , collections.Iterator)
Out[65]: False
isinstance(g , collections.Iterator)
Out[66]: True

所以这表明 b/w Iterables 和 Iterators 存在一些实现差异,但我找不到任何这样的细节,因为两者都实现了 __iter____next__ 方法,所以这种行为变化来自哪里.是不是 __iter__ for iterables 返回的东西与 __iter__ 的 iterables(generators) 返回的东西不同。如果有人可以用一些 __iter__ 的例子来解释 Iterables 和 Iterataors,那将非常有帮助。最后是关于yield 的一些谜题,因为这是使普通函数成为生成器(因此是一种迭代器)的神奇词,那么`yield 的__iter____next__ 是什么样的。

我已尽我所能解释问题,但如果仍然缺少某些内容,请告诉我,我会尽力澄清我的问题。

【问题讨论】:

  • 因为元组和列表都是序列,所以可以随机索引。迭代器不是序列,您可以为更多的东西创建迭代器,而不仅仅是序列。就像infinite counter。序列是可迭代的,这意味着您可以为它们创建(新的)迭代器。
  • 至于为什么list没有__next__(),可迭代对象不需要有__next__();他们只需要__iter__()__iter__()返回的对象必须有一个__next__()方法。
  • 要使任何对象可迭代,您需要定义__iter____next__ 方法:不,您只需要__iter__ 方法。 迭代器需要__next__iterables不需要。
  • 换句话说:您混淆了可迭代和迭代器类型。 Iterable --> 可以可能被迭代,你可以为这个对象生成一个迭代器。 Iterator --> 进行迭代的对象。
  • Iterable -> 你使用__iter__ 方法来生成迭代器。 iterator -> 你使用__next__ 方法进行迭代。迭代器也有一个__iter__ 方法,因为这样可以更轻松地处理这两种类型(只需在其中一个上调用iter(),你就知道你有返回__next__ 方法的东西)。

标签: python python-3.x iterator iterable


【解决方案1】:

这有点不同。 iterables 有一个返回迭代器的__iter__ 方法。 迭代器有一个 __next__ 方法(通常也有 __iter__ 以便 iter() 对它们起作用 - 但这不是必需的)。

列表是可迭代的:

>>> l = [1,2,3]
>>> hasattr(l, "__iter__")
True
>>> hasattr(l, "__next__")
False
>>> l_iter = iter(l)
>>> hasattr(l_iter, "__next__")
True
>>> hasattr(l_iter, "__iter__")
True
>>> l_iter == iter(l_iter)
True

并为您提供新的迭代器,每次使用它们时都会通过 e 运行

>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]
 each time you use them

>>> list(l)
[1, 2, 3]
>>> list(l)
[1, 2, 3]
>>> iter(l) == iter(l)
False

但列表迭代器本身就是一个镜头

>>> l_iter = iter(l)
>>> list(l_iter)
[1, 2, 3]
>>> list(l_iter)
[]

生成器是一个迭代器,不是可迭代的,也是一个镜头。

>>> g = (x for x in range(3))
>>> hasattr(g, "__iter__")
True
>>> hasattr(g, "__next__")
True
>>> g == iter(g)
True
>>> 
>>> list(g)
[0, 1, 2]
>>> list(g)
[]

【讨论】:

  • 所以如果我理解正确,迭代器的__iter__ 会返回自身,这就是为什么l_iter == iter(l_iter) 返回True,但迭代器的__iter__ 每次都会返回一个新的迭代器,对吗?那么您能否举例说明列表类的__iter__ 的样子?
  • 是的。它是 python,所以人们可以做一些疯狂的事情,但迭代器返回自身的优点是内部 for 之类的东西会不断消耗数据。 Iterables 倾向于传递唯一的迭代器,以便不同的 for 不会相互干扰。我会看看我能做些什么。
猜你喜欢
  • 1970-01-01
  • 2016-04-24
  • 2021-12-14
  • 2018-03-20
  • 2019-06-01
  • 2011-04-17
  • 1970-01-01
  • 2011-06-14
  • 2010-09-15
相关资源
最近更新 更多