python-生成器

说到生成器,首先要介绍列表生成式

a = [ i +1 for i in range(10)]
print(a)

 执行结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

上面的a = [ i+1 for i in range(10)]就叫做列表生成式

通过列表生成式,我们可以直接创建一个列表。此时,列表的数据是固定的,并且可以直接访问的。但是,由于内存大小的限制,列表的容量是有限的。

并且,如果创建一个包含100w个元素的列表,不仅需要占用很大的内存空间,而且,如果仅仅需要使用其中的几个数据的话,那其余的绝大部分数据所占用的空间就白白浪费了。

所以,如果列表元素可以按照某中算法推算出来,这样我们就不必创建完成的立标,而是在循环的过程中不断推算出后续的数据,从而节省大量的空间和时间。

这种机制就是python中的生成器:generator

generator的创建:

a = (i +1 for i in range(10))
print(a)

 执行结果:

<generator object <genexpr> at 0x000001798835E2B0>

此时,a就是一个生成器了

创建List和generator的区别仅仅在于最外层的[]和()

但generator中的数据是只有在访问时才会生成的,而不是像list中已经准备好的

生成器中数据的访问方式:

1、通过next()方法访问:

print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))

  执行结果:

1
2
3
4
5
6
7
8
9
10
StopIteration

由于generator保存的是算法,每次调用next(),计算的是下一个元素的值,直到计算到最后一个元素,当没有更多的元素时,抛出StopIteration的错误

2、通过next()的方法访问生成器的数据是反人类的,一般通过for循环的方式访问:

for i in a: print(i)

 执行结果:

1
2
3
4
5
6
7
8
9
10

generator的函数实现:

我们可以使用下面的函数打印斐波那契数列(Fibonacci)

def func(max):
    a,b,n = 0,1,0
    while n < max :
        print(b)
        a,b = b, a+b
        n += 1

res = func(10)

注意,在上述函数中的赋值语句:a, b = b, a + b

等同于:

t = (b,a+b)  #t是一个元祖
a = t[0]
b = t[1]

而不是:

a = b
b = a+b

执行结果:

1
1
2
3
5
8
13
21
34
55

func函数实际上是定义了斐波拉契数列的算法规则,可以从第一个元素开始,算出后续任意的元素,这种逻辑已经非常接近于generator

要把上面的func函数转变为generator,只需要把print(b)变成yiled b即可:

def func(max):
    a,b,n = 0,1,0
    while n < max :
        yield b #保存了函数的中断状态,返回当前状态的值
        a,b = b, a+b
        n += 1

res = func(10)
print(res)

 执行结果:

<generator object func at 0x000001BA2575E2B0>

 当一个函数中包含yield关键字时,此时,这个函数就不再是一个普通的函数,而是一个generator

函数和generator的执行流程是不太一样的:

函数是顺序执行,遇到return或者最后一行函数语句就返回

但是变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次从上次返回的yield语句处继续执行

我们还可以通过yield来实现在单线程情况下的并发运算效果(协程)

#生产者消费者实例
import time

def consumer(name):
    print ('%s 准备吃包子啦!' % name)
    while True:
        bao = yield
        print ('包子[%s]来了,被[%s]吃了'% (bao,name))

def producer(name):
    c1 = consumer('A')
    c2 = consumer('B')
    c3 = consumer('C')
    c1.__next__()
    c2.__next__()
    next(c3)
    print ('开始做包子啦!!!')
    for i in range(1,10):
        time.sleep(1)
        print ('做了1个包子!')
        c1.send(i)
        c2.send(i)
        c3.send(i)
producer('HAHA')

 运行结果:

A 准备吃包子啦!
B 准备吃包子啦!
C 准备吃包子啦!
开始做包子啦!!!
做了1个包子!
包子[1]来了,被[A]吃了
包子[1]来了,被[B]吃了
包子[1]来了,被[C]吃了
做了1个包子!
包子[2]来了,被[A]吃了
包子[2]来了,被[B]吃了
包子[2]来了,被[C]吃了
做了1个包子!
包子[3]来了,被[A]吃了
包子[3]来了,被[B]吃了
包子[3]来了,被[C]吃了
做了1个包子!
包子[4]来了,被[A]吃了
包子[4]来了,被[B]吃了
包子[4]来了,被[C]吃了
做了1个包子!
包子[5]来了,被[A]吃了
包子[5]来了,被[B]吃了
包子[5]来了,被[C]吃了
做了1个包子!
包子[6]来了,被[A]吃了
包子[6]来了,被[B]吃了
包子[6]来了,被[C]吃了
做了1个包子!
包子[7]来了,被[A]吃了
包子[7]来了,被[B]吃了
包子[7]来了,被[C]吃了
做了1个包子!
包子[8]来了,被[A]吃了
包子[8]来了,被[B]吃了
包子[8]来了,被[C]吃了
做了1个包子!
包子[9]来了,被[A]吃了
包子[9]来了,被[B]吃了
包子[9]来了,被[C]吃了

猜你喜欢

转载自funny121.iteye.com/blog/2355881