python生成器


为什么用生成器?

列表生成式: 使代码更简洁、还可以执行一个函数;

[i*2 for i in range(10)]

生成 :[0,2,4,6,8,12,14,16,18]


[i*2 for i in range(10)]

相当于以下三句代码:

a = []
for i in range(10):
    a.append(i*2)


通过列表生成式,我们可以直接创一个列表。但是受到内存限制,列表容量肯定是有限的。而且创建一个包含100万个元素的列表,

不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那么后面绝大多数元素占用的空间是白白浪费的。

生成器定义

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,

从而节省大量的空间。在python中,这种一边循环一边计算的机制,称为生成器:generator。

生成器创建方法

要创建一个generator,有很多种方法。

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:


生成器内容深化


生成器被调用的时候才生成所以列表元素

a = (i*2 for i in range(10))    #生成器
print(a)
<generator object <genexpr> at 0x00000212699D1E08>


例如:访问前两个数据

a = (i*2 for i in range(10))
print(a.__next__())
print(a.__next__())


生成器只记录当前位置,只存储一个数据;

只有一个__next__()方法。在python2.0 里边是next()


取数据可以用for循环取数据;

当推算的算法比较复杂的时候,用for循环无法实现的时候,用函数来实现。


fib例子:

先看一下斐波那契数列Fibonacci简写fib,除了第一个和第二个数外,任意一个数都可以由前两个数相加得到;

如:1,1,2,3,5,8,13,21,34
def fib(max):
    n,a,b = 0,0,1
    while n < max:
        print(b)
        a,b = b,a+b
        n = n+1
    return 'done'
#10代表生成10个斐波那契数据,从1开始。
fib(10)


注意:赋值语句

    a,b = b,a+b
    相当于:
    t = (b,a+b) #t是一个tuple
    a = t[0]
    b = t[1]


上面的函数和generator仅一步之遥,只需要把print(b)改成yield b 就可以了:

def fib(max):
    n,a,b = 0,0,1
    while n < max:
        yield b
        a,b = b,a+b
        n = n+1
    return 'done'
#print(fib(10))
f = fib(10)
print(f.__next__())
print(f.__next__())
print(f.__next__())
for i in f:
    print(i)

当f.__next__取到第11个数据的时候,已经没有数据了,程序会报出一个错误done;

如果不想报错,就抓住这个异常。

def fib(max):
    n,a,b = 0,0,1
    while n < max:
        yield b     #保存了中断状态,反悔了当前的状态值;
        a,b = b,a+b
        n = n+1
    return '--done--'   #作用是异常的时候打印这个消息
#print(fib(10))
f = fib(3)
#以下为异常处理代码
while True:
    try:
        x = next(f)
        print("f:",x)
    except StopIteration as e:
        print("generaotr return value:",e.value)
        break
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

通过装饰器可以实现并行效果,使单线程也可以并发。

通过yield实现在单线程的情况下实现并发运算的效果。




例子:典型的生产者消费者模型

import time
#消费者
def consumer(name):     #consumer 消费者
    print("%s 准备吃包子啦!"%name)
    while True:
        baozi = yield
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))
c = consumer("123")    #只是变成一个生成器,而不执行;如果是函数是会被调用的。
c.__next__()         #调用
# c.__next__()
c.send("韭菜")
def producer(name):
    c = consumer("A")
    c2 = consumer("B")
    c.__next__()
    c2.__next__()
    print("开始准备吃包子啦!")
    for i in range(3):
        time.sleep(1)
        print("做了1个包子,分两半!")
        c.send(i)
        c2.send(i)
producer("abc")


这个是一个最简单的协程;协程比线程更小的一个单位;