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


当前回答

可迭代对象是可以(自然地)迭代的对象。然而,要做到这一点,你将需要一个类似迭代器对象的东西,是的,术语可能令人困惑。可迭代对象包括__iter__方法,该方法将返回可迭代对象的迭代器对象。

迭代器对象是一个实现迭代器协议的对象——一组规则。在这种情况下,它必须至少有这两个方法:__iter__和__next__。__next__方法是一个提供新值的函数。__iter__方法返回迭代器对象。在更复杂的对象中,可能有单独的迭代器,但在更简单的情况下,__iter__返回对象本身(通常返回self)。

一个iterable对象是一个列表对象。它不是一个迭代器,但它有一个__iter__方法,返回一个迭代器。你可以直接以things.__iter__()的形式调用这个方法,或者使用iter(things)。

如果你想遍历任何集合,你需要使用它的迭代器:

things_iterator = iter(things)
for i in things_iterator:
    print(i)

然而,Python会自动使用迭代器,这就是为什么你从来没有看到上面的例子。相反,你可以这样写:

for i in things:
    print(i)

自己编写迭代器可能很乏味,所以Python有一个更简单的选择:生成器函数。生成器函数不是普通的函数。不是遍历代码并返回最终结果,而是延迟代码,函数立即返回一个生成器对象。

生成器对象类似于迭代器对象,因为它实现了迭代器协议。这对于大多数目的来说已经足够好了。在其他答案中有许多生成器的例子。

简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是一个集合还是其他一些值的来源。生成器是一个简化的迭代器,它或多或少完成相同的工作,但更容易实现。

通常情况下,如果你只需要发电机,你会选择发电机。但是,如果您正在构建一个更复杂的对象,其中包含其他特性之间的迭代,则应该使用迭代器协议。

其他回答

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

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

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

iterator是一个更通用的概念:任何具有__next__方法(Python 2中的next)和__iter__方法且返回self的对象。

每个生成器都是迭代器,反之亦然。生成器是通过调用具有一个或多个yield表达式(yield语句,在Python 2.5及更早版本中)的函数来构建的,它是一个满足上一段对迭代器定义的对象。

当你需要一个具有复杂状态维护行为的类,或者想公开__next__(以及__iter__和__init__)之外的其他方法时,你可能想使用自定义迭代器,而不是生成器。大多数情况下,一个生成器(有时,对于足够简单的需求,一个生成器表达式)就足够了,而且编码更简单,因为状态维护(在合理的范围内)基本上是由框架挂起和恢复“为您完成”的。

例如,一个生成器,如:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)

或等效的生成器表达式(genexp)

generator = (i*i for i in range(a, b))

将需要更多的代码来构建自定义迭代器:

class Squares(object):
    def __init__(self, start, stop):
       self.start = start
       self.stop = stop
    def __iter__(self): return self
    def __next__(self): # next in Python 2
       if self.start >= self.stop:
           raise StopIteration
       current = self.start * self.start
       self.start += 1
       return current

iterator = Squares(a, b)

但是,当然,使用类Squares,你可以很容易地提供额外的方法。

def current(self):
    return self.start

如果您的应用程序中确实需要这些额外的功能。

可迭代对象是可以(自然地)迭代的对象。然而,要做到这一点,你将需要一个类似迭代器对象的东西,是的,术语可能令人困惑。可迭代对象包括__iter__方法,该方法将返回可迭代对象的迭代器对象。

迭代器对象是一个实现迭代器协议的对象——一组规则。在这种情况下,它必须至少有这两个方法:__iter__和__next__。__next__方法是一个提供新值的函数。__iter__方法返回迭代器对象。在更复杂的对象中,可能有单独的迭代器,但在更简单的情况下,__iter__返回对象本身(通常返回self)。

一个iterable对象是一个列表对象。它不是一个迭代器,但它有一个__iter__方法,返回一个迭代器。你可以直接以things.__iter__()的形式调用这个方法,或者使用iter(things)。

如果你想遍历任何集合,你需要使用它的迭代器:

things_iterator = iter(things)
for i in things_iterator:
    print(i)

然而,Python会自动使用迭代器,这就是为什么你从来没有看到上面的例子。相反,你可以这样写:

for i in things:
    print(i)

自己编写迭代器可能很乏味,所以Python有一个更简单的选择:生成器函数。生成器函数不是普通的函数。不是遍历代码并返回最终结果,而是延迟代码,函数立即返回一个生成器对象。

生成器对象类似于迭代器对象,因为它实现了迭代器协议。这对于大多数目的来说已经足够好了。在其他答案中有许多生成器的例子。

简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是一个集合还是其他一些值的来源。生成器是一个简化的迭代器,它或多或少完成相同的工作,但更容易实现。

通常情况下,如果你只需要发电机,你会选择发电机。但是,如果您正在构建一个更复杂的对象,其中包含其他特性之间的迭代,则应该使用迭代器协议。

这篇文章涵盖了两者之间的许多细节差异,但想在两者之间的概念差异上添加一些东西:

[…GoF书中定义的迭代器从集合中检索项,而生成器可以“凭空”生成项。这就是为什么斐波那契序列生成器是一个常见的例子:无限级数的数字不能存储在一个集合中。

Ramalho,卢西亚诺。流利的Python(第415页)。O ' reilly媒体。Kindle版。

当然,它并没有涵盖所有的方面,但我认为它给出了一个很好的概念,当一个人是有用的。

无代码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.