为什么生成器是穷尽的而列表/元组不是?
为什么生成器是穷尽的而列表/元组不是?
首先,我必须说在来到这个问题之前,我读了很多SO的帖子,因为我找不到我想要的内容,或者可能是我没有理解。
所以这里就是问题:
我有点理解可迭代对象和迭代器是什么。所以任何包含项的容器对象,如列表/元组/集合,可以迭代的对象称为可迭代对象。要迭代可迭代对象,你需要迭代器,并且它是通过__iter__
方法发生的,它会为该类型返回迭代器对象,然后在迭代器对象上调用__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 "", line 1, in 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 " ", line 1, in 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 " ", line 1, in 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
所以这表明可迭代对象和迭代器之间存在某种实现差异,但我找不到任何详细信息,因为两者都实现了__iter__
和__next__
方法,那么这种行为的变化是从哪里来的呢?所以是不是__iter__
为可迭代对象返回的内容与可迭代对象(生成器)的__iter__
返回的内容不同。如果有人能用一些可迭代对象和迭代器的__iter__
的例子来解释,那将非常有帮助。最后还有一个关于yield
的谜语,因为这是一个使普通函数成为生成器(迭代器)的神奇词语,所以yield
的__iter__
和__next__
是什么样的。
我已经尽力解释这个问题了,但如果还有遗漏的地方,请告诉我,我会尽力澄清我的问题。
生成器是一种迭代器,而列表/元组不是。这是因为生成器是一次性的,而列表/元组是可以重复使用的。
在Python中,迭代器是具有`__iter__`方法的可迭代对象。迭代器还具有`__next__`方法(通常也具有`__iter__`方法,以便`iter()`可以在其上工作,但这不是必需的)。
列表是可迭代的,但列表本身不是迭代器。列表可以通过调用`iter()`函数来获得一个迭代器。
生成器是一种特殊的迭代器,它可以通过定义一个表达式和一个循环来创建。生成器是一次性的,每次使用后都会耗尽。
因此,生成器和列表/元组之间的区别在于它们的可重复使用性。生成器是一次性的,而列表/元组可以重复使用。
这种区别的原因是迭代器的`__iter__`方法返回自身,这就是为什么`l_iter == iter(l_iter)`返回`True`的原因,而可迭代对象的`__iter__`方法每次返回一个新的迭代器。这样做的好处是,不同的`for`循环不会相互干扰。
以下是一个列表类的`__iter__`方法的示例:
class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)
在这个例子中,`__iter__`方法返回了一个新的迭代器,该迭代器使用内置的`iter()`函数来迭代列表数据。
总结起来,生成器是一种一次性的迭代器,而列表/元组是可重复使用的可迭代对象。这种区别是由于迭代器的`__iter__`方法返回自身,而可迭代对象的`__iter__`方法返回一个新的迭代器。