python3 生成器

要说生成器,就必须首先要知道列表的概念;

我们创建一个如下的列表:

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:  # 迭代到没有元素后会抛出StopIteration
        print(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, 1
    while n < max_num:
        yield b
        a, b = b, a+b
        n += 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, 1
    while n < max_num:
        yield b
        a, b = b, a+b
        n += 1
    return '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, 1
    while n < max_num:
        xxx = yield b  # 后面的b在运行到yield时被带出去,前面的xx在激活yield时被带进来
        print(xxx)
        a, b = b, a+b
        n += 1
    return '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


猜你喜欢

转载自blog.csdn.net/u012613903/article/details/79390237