为什么发电机是穷举的,列表/元组不是?

时间:2018-04-05 20:24:24

标签: python python-3.x iterator iterable

首先,我必须说我在阅读之前阅读了很多SO帖子,因为我找不到我想要的内容,或者我可能没有理解。 所以这就是

我有点理解Iterables和Iterators是什么。因此任何包含项目的Lists / Tuples / Sets之类的容器对象都可以迭代,称为Iterables。现在迭代Iterables你需要迭代器及其发生的方式是因为__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

现在因为这些列表支持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__")返回False,那么我们如何能够在迭代器对象上调用下一个方法来获取列表。

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

  3. 此外,这与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__方法,所以从这里开始行为的变化来了。因此,对于iterables,__iter__返回的内容与__iter__的iterables(生成器)返回的内容不同。如果有人可以用I __iter__的一些例子来解释Iterables和Iterataors,那将会非常有用。最后有一些关于yield的谜题,因为这是使普通函数成为生成器的神奇词(所以是一种迭代器),所以`yield的__iter____next__看起来像

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

1 个答案:

答案 0 :(得分:0)

它有点不同。 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

每次使用时都会给你新的迭代器

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