• 生成器


     1 #生成器
     2 '''
     3 通过列表生成式(列表推导式),可以直接创建一个列表
     4 但是,受到内存限制,列表容量肯定是有限
     5 而且,创建一个包含100万个元素的列表,不仅占用很大内存空间
     6 如果我们仅仅需要访问前面几个元素,那后面大多数元素占用的空间都浪费了
     7 所以,如果列表元素可以按照某种算法推算出来,那我们就可以在循环中不断推算出后续元素
     8 这样就不必创建完整的list,节省大量空间
     9 在python中,这种一边循环,一边计算的机制,成为生成器:generator
    10 
    11 得到生成器的方式:
    12 1 使用列表推导式得到生成器
    13 2 借助函数完成
    14 
    15 '''
    16 #1 要得到[0,3,6,9,12,15,18,21,,,27]
    17 newlist = [x*3 for x in range(15)]
    18 print(newlist)
    19 print(type(newlist)) #<class 'list'
    20 #得到生成器:把外面的[]换成小括号:
    21 g = (x*3 for x in range(5))
    22 print(type(g)) #<class 'generator'>
    23 
    24 #调用生成器:
    25 #方式一:用__next__()来调用生成器,每次只生成一个数,只占用当前一个数字的内存
    26 print(g.__next__())
    27 print(g.__next__())
    28 #方式二:next(生成器对象),系统内置函数
    29 print(next(g))
    30 print(next(g))
    31 print(next(g))
    32 print(next(g)) #StopIteration - 生成器产生不出更多的数字了,会抛出异常,可以用try except接收这个异常并使用for循环

    生成器第二部分:

     1 #生成器
     2 #函数中出现yield关键字,则该函数就是生成器
     3 '''
     4 用函数写生成器的步骤:
     5 1 定义一个函数,函数中使用yield关键字
     6 2 调用函数,接收一下调用的结果
     7 3 得到的结果就是生成器
     8 4 借助于next()或者__next__()来得到元素
     9 
    10 '''
    11 
    12 
    13 '''
    14 g = (x*3 for x in range(5))
    15 while True:
    16     try:
    17         e = next(g)
    18         print(e)
    19     except StopIteration as err:
    20         print('打印完成')
    21         break
    22 '''
    23 
    24 #斐波那切数列
    25 '''
    26 def func():
    27     n = 0
    28     while True:
    29         n += 1
    30         yield n #当函数中出现yield关键字,则该函数就是生成器
    31         #yield类似return,yield还表示暂停的意思,下次执行时候从刚才停的位置继续执行
    32 g = func()
    33 print(g) #<generator object func at 0x0359DC70>
    34 print(next(g))
    35 print(next(g))
    36 print(next(g))
    37 print(next(g))
    38 '''
    39 
    40 def fib(length):
    41     a,b = 0,1
    42     n = 0
    43     while n < length:
    44         #print(b)
    45         yield b #返回b,并在此处暂停
    46         a,b = b,a+b
    47         n += 1
    48     return '没有更多元素了'#什么时候能返回abc:如果生成器没有任何可以生成的内容,则把要StopIteration的报错信息放在return里
    49 g = fib(6)
    50 print(next(g))
    51 print(next(g))
    52 print(next(g))
    53 print(next(g))
    54 print(next(g))
    55 print(next(g))
    56 print(next(g))
    57 print(next(g)) #StopIteration: 没有更多元素了

    生成器第三部分:send()

     1 '''
     2 生成器方法:
     3 1 __next__(): 获取下一个元素
     4 2 send(value): 向每次生成器调用中传值,注意:第一次调用send(None),必须往里传None,才可以继续send
     5 
     6 '''
     7 
     8 def gen():
     9     i = 0
    10     while i < 5:
    11         temp = yield i #temp: None. 把下一次send过来的值赋给temp,可以打印temp的值了,第一次的赋值必须是None
    12         print('temp: ',temp)
    13         for x in range(int(temp)):
    14             print('---> ', x)
    15         print('*******************')
    16         i += 1
    17     return '没有更多的数据了'
    18 g = gen()
    19 #print(next(g))
    20 #print(next(g))
    21 #print(next(g))
    22 #print(next(g))
    23 #print(next(g))
    24 #print(next(g))
    25 
    26 #g.__next__()
    27 #g.send(): 生成器里的一个方法
    28 '''
    29 print(g.send(None)) #第一次赋值必须是None
    30 n1 = g.send('haha')
    31 print('n1: ',n1) #TypeError: can't send non-None value to a just-started generator
    32 '''
    33 print(g.send(None))
    34 n2 = g.send(3)
    35 print('n2: ',n2)
    36 n3 = g.send(2)
    37 print('n3: ',n3)

    生成器应用:

     1 #进程,线程,协程
     2 #一个进程可以包含多个线程,一个线程可以包含多个协程
     3 #协程底层就是用生成器做的
     4 
     5 '''
     6 def task1(n):
     7     for i in range(n):
     8         print('正在搬第{}块砖'.format(i))
     9 
    10 def task2(n):
    11     for i in range(n):
    12         print('正在听第{}首歌'.format(i))
    13 
    14 #任务交替运行:板一块砖,听一首歌
    15 task1(10)
    16 task2(5)
    17 '''
    18 #用生成器来让两个任务切换
    19 def task1(n):
    20     for i in range(n):
    21         print('正在搬第{}块砖'.format(i))
    22         yield #yield后面可以不接内容,表示只暂停,不往外扔东西,或只扔空
    23 
    24 def task2(n):
    25     for i in range(n):
    26         print('正在听第{}首歌'.format(i))
    27         yield None
    28 
    29 g1 = task1(5)
    30 g2 = task2(5)
    31 
    32 while True:
    33     try:
    34         next(g1)
    35         next(g2)
    36     except:
    37         print('没事啦')
    38         break

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

    https://blog.csdn.net/mieleizhi0522/article/details/82142856

    先把yield理解为return

    #!/usr/bin/python3.5
    # -*- coding=utf-8 -*-
    
    def foo():
    	print('starting...')
    	while True:
    		res = yield 4
    		print('res:',res)
    
    g=foo()
    print(next(g))
    print('*'*20)
    print(next(g))
    

      

    root@ubuntu:/home/vigossr/exercise# ./yield_ex.py
    starting...
    4
    ********************
    res: None
    4

    我直接解释代码运行顺序,相当于代码单步调试:

    1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

    2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

    3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

    4.程序执行print("*"*20),输出20个*

    5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

    6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

    到这里你可能就明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

    #!/usr/bin/python3.5
    # -*- coding=utf-8 -*-
    
    def foo():
    	print('starting...')
    	while True:
    		res = yield 4
    		print('res:',res)
    
    g=foo()
    print(next(g))
    print('*'*20)
    #print(next(g))
    print(g.send(7))
    

      root@ubuntu:/home/vigossr/exercise# ./yield_ex.py 

    starting...
    4
    ********************
    res: 7
    4
    root@ubuntu:/home/vigossr/exercise#

    先大致说一下send函数的概念:此时你应该注意到上面那个的紫色的字,还有上面那个res的值为什么是None,这个变成了7,到底为什么,这是因为,send是发送一个参数给res的,因为上面讲到,return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次(return 4之后)执行,先把7赋值给了res,然后执行next的作用,遇见下一回的yield,return出结果后结束。

    5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

    6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

    7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

  • 相关阅读:
    Tom和Jerry来了,Tom和Jerry走了——北漂18年(38)
    查找(一)史上最简单清晰的红黑树解说
    HDU4763-Theme Section(KMP+二分)
    Loadrunner得到server參数
    hdu 4002
    javascript——从「最被误解的语言」到「最流行的语言」
    【云快讯】之四十八《IBM和Cisco最新收购,加强Openstack易用能力》
    2013级学生两学期编程量统计
    Hadoop高速入门
    HDOJ 3339 In Action
  • 原文地址:https://www.cnblogs.com/vigossr/p/11376327.html
Copyright © 2020-2023  润新知