列表生成式
需求:列表[0,1,2,3,4,5,6,7,8,9]每个值加1,如何实现?
方法1:列表追加
1 >>> a = [0,1,2,3,4,5,6,7,8,9] 2 >>> b = [] 3 >>> for i in range(10): 4 ... b.append(i+1) 5 ... 6 >>> b 7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 8 >>> a = b 9 >>> a 10 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方法2:原值修改enumerate
1 >>> a = [0,1,2,3,4,5,6,7,8,9] 2 >>> for index,i in enumerate(a): 3 ... a[index] += 1 4 ... 5 >>> a 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方法3:map函数
1 >>> a = [0,1,2,3,4,5,6,7,8,9] 2 >>> a = map(lambda x:x+1,a) 3 >>> a 4 <map object at 0x000000000299FF60> 5 >>> for i in a: print(i) 6 ... 7 1 8 2 9 3 10 4 11 5 12 6 13 7 14 8 15 9 16 10
方法4:列表生成,作用:使代码更简洁
1 >>> a = [i+1 for i in range(10)] 2 >>> a 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成器
当list列表很大,比如100万条,只使用几个,而不是使用那么多,如果全部放进内容占用空间很大,使用一种机制:边循环边计算,称为生成器。
元组:
1 >>> b = (i*2 for i in range(10)) 2 >>> b 3 <generator object <genexpr> at 0x00000000029981A8> 4 >>> for i in b: 5 ... print(i) 6 ... 7 0 8 2 9 4 10 6 11 8 12 10 13 12 14 14 15 16 16 18
列表:
1 >>> c = [i*2 for i in range(10)] 2 >>> c 3 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
两者区别:最外层的[]和(),一个是list,一个是generator生成器。
如何打印其中的元素:
列表list:
1 >>> c[9] 2 18 3 >>> len(c) 4 10
元组generator:生成器使用元组,只有被调用了才会生成数据,调用哪生成哪。调用方法:next()
1 >>> b[9] #生成器没有像列表一样的下标操作 2 Traceback (most recent call last): 3 File "<stdin>", line 1, in <module> 4 TypeError: 'generator' object is not subscriptable
1 >>> b = (i*2 for i in range(10)) 2 >>> next(b) 3 0 4 >>> next(b) 5 2 6 >>> next(b) 7 4 8 >>> next(b) 9 6 10 >>> next(b) 11 8 12 >>> next(b) 13 10 14 >>> next(b) 15 12 16 >>> next(b) 17 14 18 >>> next(b) 19 16 20 >>> next(b) 21 18 22 >>> next(b) #next只记录当前位置,调用到最后一个再调用就会报错 23 Traceback (most recent call last): 24 File "<stdin>", line 1, in <module> 25 StopIteration
next()方法调用到最后一个后再调用就会抛出StopIteration错误异常,正确的方法:
1 >>> g = ( i*2 for i in range(10)) 2 >>> for n in g: 3 ... print(n) 4 ... 5 0 6 2 7 4 8 6 9 8 10 10 11 12 12 14 13 16 14 18
举例1:斐波那契数列1,1,2,3,5,8,13...,函数代码为
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b 6 n = n + 1 7 return 'done' 8 9 fib(10) 10 1 11 1 12 2 13 3 14 ... 15 55
赋值语句:a,b = b,a+b 相当于 t=(b,a+b) a=t[0] b=t[1]
生成器写法:
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 #print(b) 5 yield(b) #有yield关键字就不是普通函数,而是一个生成器generator 6 a, b = b, a + b 7 n = n + 1 8 return '异常了' #11个next方法执行后,就会抛出异常Stop Iteration:异常了,所以return返回的是异常消息 9 10 f=fib(10) #生成器必须被调用才能生效 11 #print(f.__next__()) #使用next方法调用 12 for i in f: #正确调用方式如下 13 print(i)
总结:有yield函数就是一个生成器,return返回异常消息。yield保存函数中断状态,想什么时候回来就什么时候回来。
生成器的作用:函数是顺序执行,遇到return或最后一行语句就结束执行;生成器在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。(断点执行下看看)
yield实现单线程情况下并发!!!
举例2:典型的生产者消费者模型
1 import time 2 def consumer(name): 3 print("%s准备吃包子了"%name) 4 while True: 5 baozi = yield #没有yield返回值,表示为空 6 print("包子[%s]来了,被[%s]吃了"%(baozi,name)) 7 c = consumer("A") 8 c.__next__() 9 执行结果: 10 A准备吃包子了 11 12 再添加一行代码: 13 c.__next__() 14 包子[None]来了,被[A]吃了 15 16 分析: 17 第一次程序执行顺序: 18 def consumer---c=consumer("A")使得c变成generator生成器---c.__next__()调用生成器--def consumer(name = "A")--print---while True---baozi=yield--返回为空退出,底下的print(包子被谁吃了)不执行 19 20 第二次程序执行顺序: 21 def consumer---c=consumer("A")使得c变成generator生成器---c.__next__()调用生成器--def consumer(name = "A")--print---while True---baozi=yield--第二个c.__next__()---baozi=yield---print(包子被谁吃了)---baozi=yield---返回为空退出
注:生成器只有g.__next()__方法调用,同t = next(g)一样。
执行结果:
A准备吃包子了
包子[None]来了,被[A]吃了
1 import time 2 def consumer(name): 3 print("%s准备吃包子了"%name) 4 while True: 5 baozi = yield #没有yield返回值,表示为空 6 print("包子[%s]来了,被[%s]吃了"%(baozi,name)) 7 c = consumer("A") 8 c.__next__() 9 10 b1 = "韭菜馅" 11 c.send(b1) 12 c.__next__() 13 14 15 执行结果: 16 A准备吃包子了 17 包子[韭菜馅]来了,被[A]吃了 18 包子[None]来了,被[A]吃了 19 20 next和send区别? 21 next只是调用yield,send不仅调用还给yield传值
最终代码:单线程下的并行效果叫协程。
1 import time 2 def consumer(name): 3 print("%s准备吃包子了"%name) 4 while True: 5 baozi = yield #没有yield返回值,表示为空 6 print("包子[%s]来了,被[%s]吃了"%(baozi,name)) 7 # c = consumer("A") 8 # c.__next__() 9 # 10 # b1 = "韭菜馅" 11 # c.send(b1) 12 13 def producer(name): 14 c = consumer('A') #两个消费者 15 c2 = consumer('B') 16 c.__next__() #调用,打印“准备吃包子” 17 c2.__next__() 18 print("开始准备做包子了") 19 for i in range(10): 20 time.sleep(1) #每1s钟做2个包子 21 print("做了2个包子") 22 c.send(i) 23 c2.send(i) 24 25 producer("alex") 26 27 执行结果: 28 A准备吃包子了 29 B准备吃包子了 30 开始准备做包子了 31 32 做了2个包子 33 包子[0]来了,被[A]吃了 34 包子[0]来了,被[B]吃了 35 .... 36 做了2个包子 37 包子[9]来了,被[A]吃了 38 包子[9]来了,被[B]吃了 39 40 分析:三行三行的打印,好像是并行的,类似nginx异步IO的效果