迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。


当前回答

之前的回答忽略了这一点:生成器有close方法,而典型的迭代器没有。close方法在生成器中触发StopIteration异常,该异常可能在迭代器中的finally子句中被捕获,以获得运行一些清理的机会。这种抽象使得它在大型迭代器中比简单迭代器更有用。可以像关闭文件一样关闭生成器,而不必担心下面有什么。

也就是说,我个人对第一个问题的回答是:iteratable只有__iter__方法,典型的迭代器只有__next__方法,生成器既有__iter__又有__next__,还有一个附加的close。

For the second question, my personal answer would be: in a public interface, I tend to favor generators a lot, since it’s more resilient: the close method an a greater composability with yield from. Locally, I may use iterators, but only if it’s a flat and simple structure (iterators does not compose easily) and if there are reasons to believe the sequence is rather short especially if it may be stopped before it reach the end. I tend to look at iterators as a low level primitive, except as literals.

对于控制流而言,生成器是一个与承诺同样重要的概念:两者都是抽象的和可组合的。

其他回答

对于相同的数据,你可以比较两种方法:

def myGeneratorList(n):
    for i in range(n):
        yield i

def myIterableList(n):
    ll = n*[None]
    for i in range(n):
        ll[i] = i
    return ll

# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
    print("{} {}".format(i1, i2))

# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

此外,如果检查内存占用,生成器占用的内存要少得多,因为它不需要同时将所有值存储在内存中。

添加一个答案,因为现有的答案都没有专门解决官方文献中的困惑。

生成器函数是用yield而不是return定义的普通函数。当被调用时,生成器函数返回一个生成器对象,这是一种迭代器——它有一个next()方法。当调用next()时,将返回生成器函数产生的下一个值。

函数或对象都可以被称为“生成器”,这取决于你阅读的Python源文档。Python术语表表示生成器函数,而Python wiki表示生成器对象。Python教程成功地在三句话中暗示了这两种用法:

生成器是用于创建迭代器的简单而强大的工具。它们像常规函数一样编写,但在需要返回数据时使用yield语句。每次在它上调用next()时,生成器都会从停止的地方恢复(它会记住所有的数据值和最后执行的语句)。

前两句话用生成器函数标识生成器,而第三句话用生成器对象标识它们。

尽管存在这些困惑,但人们可以从Python语言参考中找到明确的最终答案:

yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用yield表达式足以导致该定义创建一个生成器函数,而不是普通函数。 当调用generator函数时,它返回一个称为generator的迭代器。然后,该生成器控制生成器函数的执行。

因此,在正式和精确的用法中,“generator”不合格指的是生成器对象,而不是生成器功能。

上面的参考资料是针对Python 2的,但Python 3语言参考资料也说了同样的事情。然而,Python 3术语表指出

发电机……通常指生成器函数,但在某些上下文中也可能指生成器迭代器。在意图不明确的情况下,使用完整的术语可以避免歧义。

无代码4行小抄:

A generator function is a function with yield in it.

A generator expression is like a list comprehension. It uses "()" vs "[]"

A generator object (often called 'a generator') is returned by both above.

A generator is also a subtype of iterator.

每个人都有一个非常漂亮和冗长的答案,我真的很感激。我只是想给那些在概念上还不太清楚的人一个简短的回答:

If you create your own iterator, it is a little bit involved - you have to create a class and at least implement the iter and the next methods. But what if you don't want to go through this hassle and want to quickly create an iterator. Fortunately, Python provides a short-cut way to defining an iterator. All you need to do is define a function with at least 1 call to yield and now when you call that function it will return "something" which will act like an iterator (you can call next method and use it in a for loop). This something has a name in Python called Generator

希望这能澄清一点。

之前的回答忽略了这一点:生成器有close方法,而典型的迭代器没有。close方法在生成器中触发StopIteration异常,该异常可能在迭代器中的finally子句中被捕获,以获得运行一些清理的机会。这种抽象使得它在大型迭代器中比简单迭代器更有用。可以像关闭文件一样关闭生成器,而不必担心下面有什么。

也就是说,我个人对第一个问题的回答是:iteratable只有__iter__方法,典型的迭代器只有__next__方法,生成器既有__iter__又有__next__,还有一个附加的close。

For the second question, my personal answer would be: in a public interface, I tend to favor generators a lot, since it’s more resilient: the close method an a greater composability with yield from. Locally, I may use iterators, but only if it’s a flat and simple structure (iterators does not compose easily) and if there are reasons to believe the sequence is rather short especially if it may be stopped before it reach the end. I tend to look at iterators as a low level primitive, except as literals.

对于控制流而言,生成器是一个与承诺同样重要的概念:两者都是抽象的和可组合的。