生成器 generator
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的
生成器可以通过一边循环一边计算的机制,在循环的过程中不断推算出后续的元素,不必创建完整的list,从而节省大量的空间
简单的 generator 定义例子:
# 把一个列表生成式的[]改成(),就创建了一个generator
g = (x * x for x in range(10))
通过next()函数可获得generator的下一个返回值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration
的错误
next(g)
使用for循环获得generator的返回值,因为generator也是可迭代对象。使用for循环不需要关心 StopIteration
的错误
for n in g:
print(n)
用类似列表生成式的for循环无法实现的时候,还可以用函数来实现 generator。如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个 generator
// 输出斐波那契数的 generator
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
# 和前面一样,获取返回值的两种方式
f = fib(6)
next(f)
for n in fib(6):
print(n)
generator 和函数的执行流程区别:
- 函数是顺序执行,遇到return语句或者最后一行函数语句就返回
- generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
如果想要拿到 generator 的return语句的返回值,必须捕获 StopIteration
错误,返回值包含在 StopIteration
的 value 中。因为for循环获取不到 StopIteration
错误,所以只能用 next():
g = fib(6)
while True:
try:
x = next(g)
print('g:',x)
except StopIteration as e:
print('Generator return value:', e.value)
break
迭代器 Iterator
可以被 next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
Iterator 对象可以 被next() 函数调用并不断返回下一个数据,直到没有数据时抛出 StopIteration 错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算
因此,generator 都是 Iterator 对象,但 list、dict、str 虽然是 Iterable ,却不是 Iterator
用 isinstance()
判断一个对象是否是 Iterator 对象:
from collections import Iterator
isinstance((x for x in range(10)), Iterator) ==> True
isinstance([], Iterator) ==> False
isinstance({}, Iterator) ==> False
isinstance('abc', Iterator) ==> False
可以使用 iter()
函数把list、dict、str等 Iterable
变成 Iterator
isinstance(iter([]), Iterator) ==> True
isinstance(iter('abc'), Iterator) ==> True