Python的生成器与迭代器的区别
迭代器和生成器有什么区别呢?如果能给一些使用它们的例子就更好了。
15 个回答
迭代器 是一种对象,它通过 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
迭代器和生成器有什么区别?举一些使用场景的例子会更有帮助。
简单来说,迭代器是有 __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
语句。
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
如果你的应用程序确实需要这样的额外功能。