要说生成器,就必须首先要知道列表的概念;
我们创建一个如下的列表:
ls = [1,2,3,4,5,6,7,8,9]
那么就开辟了一个门牌号为ls的内存区,然后真的把1,2,3,4,5,6,7,8,9这几个数字放到了内存中;
如果我们在for循环中要根据i的值来获取一个值的话,我们可以把数据放在上面的ls中,然后通过ls[i]来获取;这样的
方式存在巨大的缺点,就是要事先准备好ls中的所有数值;如果很多的话会造成响应慢,内存溢出等问题;
所以就有有了生成器的概念,生成器会指定一个公式,然后每次循环都根据公式计算出当前的值,不用事先生成
所有的值。
一、最简单的生成器
# -*- coding:utf-8 -*-
# Author: Evan Mi# 这是一个最基本的生成器,对应的公式是i*2,i从0到9
# 这样我们就可以通过迭代的方式来访问生成的每一个值了
# 它的缺点就是,只能逐一迭代,不能随意指定i,也不能往前迭代
my_gen_01 = (i*2 for i in range(10))
"""
可以看到my_gen是一个generator对象
"""
print(my_gen_01) # <generator object <genexpr> at 0x00000000024FCBA0>
# generator对象是一个迭代器对象,所以直接通过next()方法来迭代
while True:try:print(next(my_gen_01)) # 迭代(根据公式计算)except StopIteration as e: # 迭代到没有元素后会抛出StopIterationprint(e.value)break
"""
运行结果:
0
2
4
6
8
10
12
14
16
18
None
"""
# 用for循环迭代,不会有异常抛出
# 需要新建一个,因为上一个已经被迭代完了(生成器是一次性用品)
my_gen_02 = (i*2 for i in range(10))for val in my_gen_02:print(val)
"""
运行结果:
0
2
4
6
8
10
12
14
16
18
"""
二、用函数来实现复杂的生成器
# -*- coding:utf-8 -*-
# Author: Evan Mi# 引用别人的斐波那契数列生成函数
"""函数运行到yield的时候,就会暂停,并将yield关键字之后的值传到函数外,函数暂停在这里,直到等到下一次迭代"""
def fib(max_num):n, a, b = 0, 0, 1while n < max_num:yield ba, b = b, a+bn += 1# 定义了一个生成器,这个生成器每次迭代得到的值就是yield带出来的值fb = fib(10)# 用for循环迭代
for val in fb:print(val)
"""
运行结果:
1
1
2
3
5
8
13
21
34
55
"""
# 用next迭代
fb1 = fib(10)
while True:try:print(next(fb1))except StopIteration as e:print(e.value)break
"""
运行结果:
1
1
2
3
5
8
13
21
34
55
None
"""
可以看到,在使用for循环迭代的时候,并不需要自己去处理迭代停止的异常;而用next自己处理就需要;而且
我们每次的e.value都是none;现在给函数加上返回值,如下:
def fib(max_num):n, a, b = 0, 0, 1while n < max_num:yield ba, b = b, a+bn += 1return 'done'
对这样的函数,我们用next调用;
# 用next迭代
fb1 = fib(10)
while True:try:print(next(fb1))except StopIteration as e:print(e.value)break
"""
运行结果:
1
1
2
3
5
8
13
21
34
55
done
"""
可以看到e.value变成了done;所以一个函数如果作为了生成器,那么return的值就是迭代停止时的异常信息;
yield能带出函数中的值,那么yield就是函数内部与外界的媒介,那么自然也能从外部代值回来:
修改函数如下:
def fib(max_num):n, a, b = 0, 0, 1while n < max_num:xxx = yield b # 后面的b在运行到yield时被带出去,前面的xx在激活yield时被带进来print(xxx)a, b = b, a+bn += 1return 'done'
执行如下的测试:
gx = fib(10)
# 一开是只能调用next,因为这时只是创建了一个生成器,还没有yield变量
print('out;', next(gx))
# 把233给yield,同时往下运行到下一次循环到yield处,并带回yield的值
print('out;', gx.send(233))
print('out;', gx.send(333))
print('out;', gx.send(433))
# 循环到下一次的yield,并带回yield的值(其实就是把None给了yield)
print('out;', next(gx))
运行结果如下:
out; 1 第一次只能从yield带出值
233 通过yield传入了233,在函数内部打印
out; 1 带出1
333 传入333
out; 2 带出2
433 传入433
out; 3 带出3
None 传入None
out; 5 带出5