zoukankan      html  css  js  c++  java
  • Python生成器

    Python生成器generator是一个有意思的东西,首先来看:

    通过把列表生产式的[ ]改为(),弄了一个生成器出来,不用生成很占内存的列表了,在有些情况下就可以这样使用,但是,一旦采用list()方法或者for循环提取值或者next()方法,生成器里的值就会不断减少,就像出栈那样

    再来看下面的这个:

    def MyGenerator():
        print('aaa')
        for n in range(1, 4):
            print('hhh')
            yield n
        return "done"
    
    print(MyGenerator)
    
    
    Num=MyGenerator()
    print(Num)
    print(next(Num))
    print(next(Num))
    print(next(Num))

    结果:

    <function MyGenerator at 0x0000017DC6D240D0>
    <generator object MyGenerator at 0x0000017DC6CFDB48>
    aaa
    hhh
    1
    hhh
    2
    hhh
    3

    前两个print没问题,一个是函数,后面是生成器对象(实例)

    第一个next(Num)说明生成器一开始启动之后,执行了打印'aaa',然后打印'hhh',然后到yield的地方就跳出,把yield后面那个n(n此时是1)返回并打印;

    第二个next(Num)说明生成器并不会去打印'aaa'了,而是会打印'hhh',并且把n(此时是2)返回并打印;第三个 next(Num)同理

    由上我们可以知道next函数会启动生成器,并且得到yield后面的值,但并不是让生成器从头开始执行,而是到yield那个地方执行,包括for循环在内。

    作一个变化:

    def MyGenerator():
        print('aaa')
        for n in range(1, 4):
            print('hhh')
            yield n
            print('bbb')
        return "done"
    
    #print(MyGenerator)
    
    
    Num=MyGenerator()
    #print(Num)
    #while True:
    #print(next(Num))
    #print(next(Num))
    print(next(Num))
    aaa
    hhh
    1

    接着连续打印两个:

    def MyGenerator():
        print('aaa')
        for n in range(1, 4):
            print('hhh')
            yield n
            print('bbb')
        return "done"
    
    #print(MyGenerator)
    
    
    Num=MyGenerator()
    #print(Num)
    #while True:
    #print(next(Num))
    print(next(Num))
    print(next(Num))
    aaa
    hhh
    1
    bbb
    hhh
    2

    代码在第一个的基础上多执行了打印'bbb','hhh'和2,说明了什么,说明第二个next启动时,代码从上次执行完的地方开始向下执行,并且总是到yield的地方就把n抛出,不再执行下去

    接着再做一次变化:

    def MyGenerator():
        print('aaa')
        for n in range(1, 4):
            print('hhh')
            yield n
            print('bbb')
        return "done"
    
    #print(MyGenerator)
    
    
    Num=MyGenerator()
    #print(Num)
    for i in range(4):
        print('这是第%d次哦'%i)
        print(next(Num))
    这是第0次哦
    aaa
    hhh
    1
    这是第1次哦
    bbb
    hhh
    2
    这是第2次哦
    bbb
    hhh
    3
    这是第3次哦
    bbb
    Traceback (most recent call last):
      File "D:PostgraduatePythonPython爬取美国商标局专利异步爬虫yield_consumer_producer.py", line 56, in <module>
        print(next(Num))
    StopIteration: done

    明白了吧,当i=3的时候,也就是第四次启动next进入生成器,接着上一次代码停止的地方也就是yield n(n是3),然后执行打印'bbb',但是生成器里的for循环已经结束了,yield没有可以抛出去的东西了,所以报错了

    再来看下面的代码:

    import time  
    def func(n):
        for i in range(0, n):  
            arg = yield i
            #arg = 3
            print('func:',arg)  
      
    f = func(3)
    for i in range(4):
        print('这是第%d次哦'%i)
        print('main_next:', next(f))  
    #print('main_send:', f.send(100))  
    time.sleep(0.1)
    这是第0次哦
    main_next: 0
    这是第1次哦
    func: None
    main_next: 1
    这是第2次哦
    func: None
    main_next: 2
    这是第3次哦
    func: None
    Traceback (most recent call last):
      File "D:PostgraduatePythonPython爬取美国商标局专利异步爬虫yield_consumer_producer.py", line 35, in <module>
        print('main_next:', next(f))
    StopIteration

    过程:第0次启动next,进入生成器函数,yield 抛出0,第1次启动next,进入生成器执行上次代码yield之后的部分,也就是arg=,这部分,这也是关键所在,而arg传入的值是什么呢,不是yield i 这个东西,而是next传进去的东西,而next传入的是None,因此打印出来就是None,接着生成器函数继续执行,i是1了,到了yield的地方,继续把i抛出,给next,把main_next打印出来;同理,接下去第2次启动next。。直到生成器的for循环结束,yield没有东西可以抛,就出现异常了

    紧接着上面的代码,我们把send加进去看看

    import time  
    def func(n):
        for i in range(0, n):  
            arg = yield i
            #arg = 3
            print('func:',arg)  
      
    f = func(3)
    for i in range(4):
        print('这是第%d次哦'%i)
        print('main_next:', next(f))  
        print('main_send:', f.send(100))  
    time.sleep(0.1)
    这是第0次哦
    main_next: 0
    func: 100
    main_send: 1
    这是第1次哦
    func: None
    main_next: 2
    func: 100
    Traceback (most recent call last):
      File "D:PostgraduatePythonPython爬取美国商标局专利异步爬虫yield_consumer_producer.py", line 36, in <module>
        print('main_send:', f.send(100))
    StopIteration

    我们来分析一下过程:

    第一次启动next,进入生成器函数,i是0,yield抛出i,打印main_next:0;

    第一次启动send,紧接着上次代码挂起的地方,把send的参数100给yield i(这里非常关键),也就是说arg被赋值为100,接下去打印func:100,并接着执行当i是1的时候,yield抛出i,给send,打印main_send:1;

    第二次启动next,进入生成器函数,这时执行arg=这条语句,但是next传进去的参数是None,于是func打印出None,接着生成器函数继续执行下去,i是2了,yield抛出2给next,打印main_next:2;

    第二次启动send,进入生成器函数,这时执行arg=这条语句,把send的参数100给arg,打印func:100

    关键之处就在于arg = yield这条语句的理解以及send(100)里面的参数传递问题

    注意的地方:第一次启动生成器最好用next,不要用send,更不要用带参数的send,那就直接报错了

    import time  
    def func(n):
        for i in range(0, n):  
            arg = yield i
            #arg = 3
            print('func:',arg)  
      
    f = func(3)
    for i in range(4):
        print('这是第%d次哦'%i)
        #print('main_next:', next(f))  
        print('main_send:', f.send(100))  
    time.sleep(0.1)

    yield from就是为了让yield更好地使用,避免报错,且容易得到抛出的值

    import time  
    def func(n):
        yield from range(0,n)
            
      
    f = func(3)
    print(list(f))
    f = func(3)
    for g in f:
        print(g)
    [0, 1, 2]
    0
    1
    2

    注意:第一次list执行之后,生成器里就没有东西了

    人生苦短,何不用python
  • 相关阅读:
    SharedPreferences
    短信发送器的实现
    第四周总结
    本周开发工作时间及内容
    自我总结
    随笔
    结对编程
    目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点?
    八皇后
    数制转换
  • 原文地址:https://www.cnblogs.com/yqpy/p/8746931.html
Copyright © 2011-2022 走看看