Python的生成器与迭代器的区别

793 投票
15 回答
268727 浏览
提问于 2025-04-15 22:24

迭代器和生成器有什么区别呢?如果能给一些使用它们的例子就更好了。

15 个回答

57

迭代器 是一种对象,它通过 next() 方法来获取序列中的下一个值。

生成器 是一种函数,它使用 yield 关键字来产生一系列的值。

每次对生成器对象(比如下面的 f)调用 next() 方法时,都会生成序列中的下一个值,这个生成器对象是由生成器函数(比如下面的 foo())返回的。

当你调用一个生成器函数时,它会返回一个生成器对象,但这个函数并不会立即执行。当第一次调用 next() 方法时,函数开始执行,直到遇到 yield 语句,这时会返回一个值。yield 会记录下发生了什么,也就是说,它会记住上一次的执行状态。接下来,再次调用 next() 方法时,会从上一次的值继续执行。

下面的例子展示了 yield 和对生成器对象调用 next 方法之间的互动。

>>> def foo():
...     print("begin")
...     for i in range(3):
...         print("before yield", i)
...         yield i
...         print("after yield", i)
...     print("end")
...
>>> f = foo()
>>> next(f)
begin
before yield 0            # Control is in for loop
0
>>> next(f)
after yield 0             
before yield 1            # Continue for loop
1
>>> next(f)
after yield 1
before yield 2
2
>>> next(f)
after yield 2
end
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
213

迭代器和生成器有什么区别?举一些使用场景的例子会更有帮助。

简单来说,迭代器是有 __iter____next__(在 Python 2 中是 next)方法的对象。生成器则是创建迭代器实例的一种简单、内置的方法。

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

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

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

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

如果想要更详细的解释和例子,可以继续往下看。

生成器 迭代器

具体来说,生成器是迭代器的一种子类型。

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

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

>>> 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__() 在 Python 3 中] 方法。关于生成器的更多信息可以在 yield 表达式的文档中找到。

(强调部分。)

所以我们可以得出,生成器是一种(方便的)迭代器。

示例迭代器对象

你可以通过创建或扩展自己的对象来实现迭代器协议。

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

但更简单的方法是直接使用生成器:

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 对象,使其可以被迭代时,可以直接使用迭代器协议。

然而,在绝大多数情况下,使用 yield 定义一个返回生成器迭代器的函数,或者考虑生成器表达式,会更合适。

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

804

iterator(迭代器)是一个更广泛的概念:任何有 __next__ 方法(在 Python 2 中是 next)和 __iter__ 方法并且这个方法返回 self 的对象,都可以称为迭代器。

每个生成器都是一个迭代器,但反过来不一定成立。生成器是通过调用一个包含一个或多个 yield 表达式(在 Python 2.5 及更早版本中是 yield 语句)来创建的,它是一个符合前面提到的迭代器定义的对象。

当你需要一个类来处理比较复杂的状态,或者想要提供除了 __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

如果你的应用程序确实需要这样的额外功能。

撰写回答