Python--生成器


学习生成器之前,首先需要认识列表生成式,直奔主题。

1、简单列表生成式示例:

1 b = [ i for i in range(10)]
2 print(b)
3 
4 >>>
5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

看样子好像很NB的样子,其实它等价于:

1 c = []
2 for i in range(10):
3     c.append(i)
4 print(c)
5 
6 >>>
7 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

其实还是有点NB的,毕竟代码少了,手动嘻嘻嘻!

2、削微高级点的列表生成式:

1 def func(x):
2     a = x + 1
3     b = a + x
4     return b
5 d = [ func(i) for i in range(10) ]
6 print(d)
7 
8 >>>
9 [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

生成式生成的数据可以交给函数处理,进而得到预定规则的一组数据。

下面有请主角登场!!!!

生成器:在Python中,一边循环一边计算的机制,称为生成器:generator。

  我们知道了,通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。

创建生成器的姿势:将生成式的"[]"换成"()"就ok了,是不是超级简单。。。

 1 a = ( i for i in range(10))
 2 print(a)
 3 for i in a:
 4     print(i)
 5 
 6 >>>
 7  at 0x00000270A14EB0B0>
 8 0
 9 1
10 ..
11 9

由上面的例子可见,打印的a已不是一个列表,而是a的内存地址。而且a可以通过for循环将其中的值取出。

就用一个小栗子完成生成式和生成器比较吧:

1 # 生成b需要很长时间
2 b = [ i for i in range(100000000) ]
3 # 生成c很快,因为不需要实际写数据,c的数据是在使用时才生成
4 c = ( i for i in range(100) )

生成器特性:

1.生成器只能一个个的取数据
2.生成器只有在调用时才会生成相应的数据
3.生成器只记住当前位置的地址,之前的的内存地址都没了
4.只有一个__next__()方法
5.生成器不能根据索引位置取值
1 c = ( i for i in range(100) )
2 print(c.__next__())
3 print(c.__next__())
4 print(c[0])
5 
6 >>>
7 0
8 1
9 TypeError: 'generator' object is not subscriptable

#############################################################

示例:了解斐波那契数列(下个数为前两个数的和)

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         print(b)
 5         a, b = b, a+b  #注意a, b = b, a+b 等价于 t(b,a+b);a=t[0];b=t[1],而不是 a=b;b=a+b
 6         n = n + 1
 7     return "done"
 8 fib(7)
 9 >>>
10 1
11 1
12 2
13 3
14 5
15 8
16 13

使斐波那契数列变成一个生成器

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         yield b  #yield返回当前状态的值,程序停在此处
 5         a, b = b, a+b
 6         n = n + 1
 7     return "---done---" 
 8 # fib(5)  #此时执行,并无返回值,因为没有取值。使用__next__()进行取值
 9 f = fib(5)
10 print(f.__next__())
11 print(f.__next__())
12 print(f.__next__())
13 print(f.__next__())
14 print(f.__next__())
15 print(f.__next__())
16 
17 >>>
18 1
19 1
20 2
21 3
22 5
23 StopIteration: ---done---  # 当超出时报错
使用yield后,可以使用__next__()方法,中断函数的执行,在中断之后可以执行其他的操作,然后还可以通过__next__()再进入
例如:
 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         # print(b)
 5         yield b  #yield返回当前状态的值,程序停在此处
 6         a, b = b, a+b
 7         n = n + 1
 8     return "---done---"  
 9 f = fib(5)
10 f.__next__()
11 X = f.__next__()
12 print(X)
13 print("休息一会儿,马上就回来了")
14 Y = f.__next__()
15 print(Y)
16 print("又走了...")
17 Z = f.__next__()
18 print(Z)
19 >>>
20 1
21 休息一会儿,马上就回来了
22 2
23 又走了...
24 3

能看到这,那你又又又又又又要学到了:抓异常、抓异常、抓异常。重要的事情说三遍!!!

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         # print(b)
 5         yield b  #yield返回当前状态的值,程序停在此处
 6         a, b = b, a+b
 7         n = n + 1
 8     return "---done---"
 9 g = fib(8)
10 while True:
11     try:
12         x = next(g)  # 等价于 x = g.__next__()
13         print("value:", x)
14     except StopIteration as e: #当出现StopIteration时执行下面的代码
15         print("Generator return value", e.value)
16         break
17 
18 >>>
19 value: 1
20 value: 1
21 value: 2
22 value: 3
23 value: 5
24 value: 8
25 value: 13
26 value: 21
27 Generator return value ---done---

生成器示例高级,实现并行:

import  time
def consum(name):
    print("%s 要来吃包子了" % (name))
    while True:
        baozi = yield
        print("%s个包子被%s吃了" % (baozi, name))

def product(name):
    d = consum("flb")
    d2 = consum("wxl")
    d.__next__() # next唤醒yield,不会给yield传值
    d2.__next__()
    print("%s要来做包子了" %(name))
    for i in  range(3):
        time.sleep(1)
        print("%s做了%s个包子" %(name,i))
        d.send(i)  # send唤醒yield,会给yield传值
        d2.send(i)
product("TJ")

>>>
flb 要来吃包子了
wxl 要来吃包子了
TJ要来做包子了
TJ做了0个包子
0个包子被flb吃了
0个包子被wxl吃了
TJ做了1个包子
1个包子被flb吃了
1个包子被wxl吃了
TJ做了2个包子
2个包子被flb吃了
2个包子被wxl吃了

END!!!