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 <generator object <genexpr> 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!!!