【发布时间】:2018-09-15 18:46:11
【问题描述】:
首先我不得不说我在阅读这篇文章之前阅读了很多 SO 帖子,因为我找不到我要找的东西,或者我可能不明白。 就这样吧
我有点理解 Iterables 和 Iterators 是什么。因此,任何包含可以迭代的项目的容器对象(如 Lists/Tuples/Sets)都称为 Iterables。现在要遍历 Iterables,您需要 Iterators,它发生的方式是因为 __iter__ 方法为您提供类型的 Iterator 对象,然后在 Iterator 对象上调用 __next__ 以提取值。
因此,要使任何对象可迭代,您需要定义 iter 和 next 方法,我认为列表也是如此。但是我最近发现的奇怪的部分来了。
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 方法来获取元素并且它可以工作。
现在,如果之前的
hasattr(a, "__next__")返回False,那么我们如何调用迭代器对象的 next 方法来获取列表。现在让我想到这一切的原始问题,无论我迭代列表多少次,它都不会耗尽,并且每次调用
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