• Day4-生成器generator


    列表生成式

    需求:列表[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]
    View Code

    方法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]
    View Code

    方法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
    View Code

    方法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
    View Code

    列表:

    1 >>> c = [i*2 for i in range(10)]
    2 >>> c
    3 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    View Code

    两者区别:最外层的[]和(),一个是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
    View Code
     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
    View Code

    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
    View Code

    举例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
    View Code

    赋值语句: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)
    View Code

    总结:有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---返回为空退出
    View Code

    注:生成器只有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传值
    View Code

    最终代码:单线程下的并行效果叫协程。

     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的效果
    View Code
  • 相关阅读:
    Python性能提升小技巧
    算法-第四版-练习1.3.32解答
    算法-第四版-练习1.3.30解答
    算法-第四版-练习1.3.31解答
    算法-第四版-练习1.3.29解答
    PAT (Basic Level) Practise:1030. 完美数列
    PAT (Basic Level) Practise:1010. 一元多项式求导
    PAT (Basic Level) Practise:1012. 数字分类
    PAT (Basic Level) Practise:1038. 统计同成绩学生
    PAT (Basic Level) Practise:1018. 锤子剪刀布
  • 原文地址:https://www.cnblogs.com/wolfs685/p/6725690.html
Copyright © 2020-2023  润新知