为什么生成器是穷尽的而列表/元组不是?

13 浏览
0 Comments

为什么生成器是穷尽的而列表/元组不是?

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

所以这里就是问题:

我有点理解可迭代对象和迭代器是什么。所以任何包含项的容器对象,如列表/元组/集合,可以迭代的对象称为可迭代对象。要迭代可迭代对象,你需要迭代器,并且它是通过__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

现在,由于列表支持迭代器协议,为什么它们的实现中缺少__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方法来获取元素,它起作用了。

  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

所以这表明可迭代对象和迭代器之间存在某种实现差异,但我找不到任何详细信息,因为两者都实现了__iter____next__方法,那么这种行为的变化是从哪里来的呢?所以是不是__iter__为可迭代对象返回的内容与可迭代对象(生成器)的__iter__返回的内容不同。如果有人能用一些可迭代对象和迭代器的__iter__的例子来解释,那将非常有帮助。最后还有一个关于yield的谜语,因为这是一个使普通函数成为生成器(迭代器)的神奇词语,所以yield__iter____next__是什么样的。

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

0
0 Comments

生成器是一种迭代器,而列表/元组不是。这是因为生成器是一次性的,而列表/元组是可以重复使用的。

在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__`方法返回一个新的迭代器。

0