为什么生成器是详尽的而 Lists/Tuples 不是?

Why Generators are exhaustive and Lists/Tuples are not?

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

我有点了解 Iterables 和 Iterators 是什么。因此,任何包含项目的容器对象,如 Lists/Tuples/Sets,您可以对其进行迭代,称为 Iterables。现在迭代 Iterables 你需要迭代器,它发生的方式是因为 __iter__ 方法为你提供类型的迭代器对象,然后调用迭代器对象上的 __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

现在因为列表确实支持 Iterator 协议,为什么 __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__") returns 一个 False 那么我们如何能够调用迭代器对象上的 next 方法来获取列表。

  2. 现在让我思考所有这些的原始问题,无论我迭代列表多少次,它都不会耗尽并且调用 iter() 返回一个新的迭代器对象每次,但在生成器的情况下,这不会发生,一旦生成器耗尽,无论你调用多少次 iter() 它总是会给你已经引发 [=21= 的同一个对象] 例外,这也是真的,因为迭代器一旦引发 StopIteration,它总是会,但为什么它不会发生在列表中。

此外,这与 python 文档对 conatiner.__ iter__ that container.__iter__ gives you the iterator object for the type and iterator.__ iter__ 所说的同步,iterator.__iter__ 为您提供了迭代器对象本身,这正是调用 iter() 在生成器上 returns 一遍又一遍地使用相同的对象。但为什么,更重要的是如何?

这里还要注意的一件事是

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

所以这表明存在一些实现差异 b/w 可迭代对象和迭代器,但我找不到任何此类细节,因为它们都实现了 __iter____next__ 方法,所以这种行为变化从何而来。因此,__iter__ for iterables returns 与 iterables(generators)的 __iter__ 返回的内容不同。如果有人可以用一些 __iter__ 的示例来解释 Iterables 和 Iterataors,那将非常有帮助。最后是关于 yield 的一些困惑,因为这是使普通函数成为生成器(因此是一种迭代器)的神奇词,所以 `yield 的 __iter____next__ 看起来像什么.

我已经尽力解释了这个问题,但如果还有什么遗漏,请告诉我,我会尽力澄清我的问题。

这有点不同。 iterables 有一个 __iter__ 方法,returns 一个迭代器。 迭代器 有一个 __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)
[]

generator是iterator,不是iterable,也是one shot

>>> 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)
[]