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


当前回答

所有生成器都是迭代器,反之亦然。

from typing import Iterator
from typing import Iterable
from typing import Generator

class IT:

    def __init__(self):
        self.n = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.n == 4:
            raise StopIteration
        try:
            return self.n
        finally:
            self.n += 1


def g():
    for i in range(4):
        yield i

def test(it):
    print(f'type(it) = {type(it)}')
    print(f'isinstance(it, Generator) = {isinstance(it, Generator)}')
    print(f'isinstance(it, Iterator) = {isinstance(it, Iterator)}')
    print(f'isinstance(it, Iterable) = {isinstance(it, Iterable)}')
    print(next(it))
    print(next(it))
    print(next(it))
    print(next(it))
    try:
        print(next(it))
    except StopIteration:
        print('boom\n')


print(f'issubclass(Generator, Iterator) = {issubclass(Generator, Iterator)}')
print(f'issubclass(Iterator, Iterable) = {issubclass(Iterator, Iterable)}')
print()
test(IT())
test(g())

输出:

issubclass(Generator, Iterator) = True
issubclass(Iterator, Iterable) = True

type(it) = <class '__main__.IT'>
isinstance(it, Generator) = False
isinstance(it, Iterator) = True
isinstance(it, Iterable) = True
0
1
2
3
boom

type(it) = <class 'generator'>
isinstance(it, Generator) = True
isinstance(it, Iterator) = True
isinstance(it, Iterable) = True
0
1
2
3
boom

其他回答

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

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

之前的回答忽略了这一点:生成器有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.

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

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

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

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

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

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

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

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

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

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

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

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

总结:迭代器是具有__iter__和__next__ (Python 2中的next)方法的对象。生成器提供了一种简单的内置方法来创建iterator实例。

包含yield的函数仍然是一个函数,当调用它时,返回一个生成器对象的实例:

def a_function():
    "when called, returns generator object"
    yield

生成器表达式也返回一个生成器:

a_generator = (i for i in range(0))

有关更深入的阐述和示例,请继续阅读。

Generator是一个迭代器

具体来说,generator是迭代器的子类型。

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

我们可以通过几种方式创建生成器。一种非常常见和简单的方法是使用函数。

具体来说,包含yield的函数是一个函数,当调用它时,返回一个生成器:

>>> def a_function():
        "just a function definition with yield in it"
        yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function()  # when called
>>> type(a_generator)           # returns a generator
<class 'generator'>

生成器也是一个迭代器:

>>> isinstance(a_generator, collections.Iterator)
True

迭代器是可迭代对象

迭代器是可迭代对象,

>>> issubclass(collections.Iterator, collections.Iterable)
True

它需要一个返回迭代器的__iter__方法:

>>> collections.Iterable()
Traceback (most recent call last):
  File "<pyshell#79>", line 1, in <module>
    collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__

一些可迭代对象的例子是内置元组、列表、字典、集合、冻结集、字符串、字节字符串、字节数组、范围和memoryview:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

迭代器需要一个next或__next__方法

在Python 2中:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<pyshell#80>", line 1, in <module>
    collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next

在Python 3中:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__

我们可以使用iter函数从内置对象(或自定义对象)中获取迭代器:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

当你试图使用for循环对象时,__iter__方法会被调用。然后在迭代器对象上调用__next__方法,为循环取出每一项。迭代器在耗尽它时抛出StopIteration,此时它不能被重用。

来自文档

从内置类型文档的迭代器类型部分的生成器类型部分:

Python的生成器提供了一种实现迭代器协议的方便方法。如果容器对象的__iter__()方法被实现为生成器,它将自动返回一个迭代器对象(技术上,一个生成器对象),提供__iter__()和next() [__next__() in python3]方法。关于生成器的更多信息可以在yield表达式的文档中找到。

(强调)。

从这里我们了解到generator是一种(方便的)迭代器类型。

迭代器对象示例

您可以通过创建或扩展自己的对象来创建实现Iterator协议的对象。

class Yes(collections.Iterator):

    def __init__(self, stop):
        self.x = 0
        self.stop = stop

    def __iter__(self):
        return self

    def next(self):
        if self.x < self.stop:
            self.x += 1
            return 'yes'
        else:
            # Iterators must raise when done, else considered broken
            raise StopIteration

    __next__ = next # Python 3 compatibility

但是简单地使用Generator更容易做到这一点:

def yes(stop):
    for _ in range(stop):
        yield 'yes'

或者更简单,生成器表达式(类似于列表推导式):

yes_expr = ('yes' for _ in range(stop))

它们都可以以同样的方式使用:

>>> stop = 4             
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), 
                             ('yes' for _ in range(stop))):
...     print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...     
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes

结论

当需要将Python对象扩展为可迭代的对象时,可以直接使用Iterator协议。

然而,在绝大多数情况下,您最适合使用yield来定义返回Generator Iterator的函数或考虑Generator expression。

最后,请注意生成器作为协程提供了更多的功能。我在回答“yield”关键字做什么?”时,深入地解释了Generators和yield语句。

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

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

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