什么是生成器?
生成器(generator)是Python中提供的一种固定语法。在Python中目前存在两种生成器:生成器函数、生成器表达式。其中,使用了yield关键字的函数就被称为生成器函数,。
生成器可以使用推导式的语法创建,只需要将列表推导式的方括号[ ]改为圆括号( ),即——使用圆括号( )的推导式就是一个生成器表达式。
生成器可以说是一种特殊的迭代器,它自动实现了iter和next方法。与迭代器不同的是,生成器在迭代过程中可以改变当前的迭代值。
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 返回列表lst中的奇数
gen = (i for i in lst if i % 2 == 1)
print(gen)
# <generator object <genexpr> at 0x0000027303921A48>
for i in gen:
print(i, end=' ')
# 1 3 5 7 9
为什么要使用生成器?
众所周知,Python的性能一直被诟病,如果使用列表来存储数据,将会非常的消耗内存。生成器的话,不管数据有多少,它在内存中的大小永远都是一个生成器的大小。
import sys
lst1 = [i for i in range(1000)]
lst2 = [i for i in range(1000000)]
gen1 = (i for i in range(1000))
gen2 = (i for i in range(1000000))
print(sys.getsizeof(lst1))
# 9024
print(sys.getsizeof(lst2))
# 8697464
print(sys.getsizeof(gen1))
# 120
print(sys.getsizeof(gen2))
# 120
从上述代码应该可以看出生成器表达式和列表推导式在内存中的巨大差异了。也就是说,当我们在操作一个庞大的数据时,生成器可以帮我们节省许多的空间。
yield关键字
当一个函数使用了yield关键字,那么这个函数就会转变成一个生成器。
yield与return类似,都是用来返回结果,但不同之处在于,下次迭代时,代码会从yield的下一条语句开始执行。也就是说,每次停在哪儿,下次就从哪儿开始,yield会保存当前的运行状态。
# 实现斐波那契数列
def fibonacci(n):
a, b, dec = 1, 1, 0
while dec < n:
yield a
a, b = b, a + b
dec += 1
# 输出
def output():
n = int(input('请输入斐波那契数列的长度:'))
fib = fibonacci(n)
print(fib)
# <generator object fibonacci at 0x000002A0688F1A48>
for i in fib:
print(i, end=' ')
# 1 1 2 3 5......
if __name__ == '__main__':
output()
send()方法
熟悉迭代器的小伙伴应该知道,next方法可以访问迭代器的下一个可用元素。在生成器中,next和send的作用是类似的,不同之处在于,send可以传递yield表达式的值进去。也就是说,next()和send(None)的作用是相同的。
# 通过send实现一个累加算法
def func():
sum = 0
while True:
des = yield sum
sum += des
result = func()
print(next(result))
# 0
print(result.send(1))
# 1
print(result.send(2))
# 3
print(result.send(3))
# 6
需要注意的是,第一次调用时,需要使用next()语句或是send(None),不能使用send发送一个非None的值,否则会报错的。因为,第一次执行生成器的时候,它并没有执行到yield这个位置,所以没有Python yield语句来接收这个值。