zoukankan      html  css  js  c++  java
  • 【Python】迭代器和生成器的个人理解,再讲一讲协程

      在认识yield的时候,网上很多文章都是说这个是个生成器,但是我并不知道这个是用来做什么的,所以概念很快就忘记了,后面读了几个文章以后感觉茅塞顿开。我就接介绍一下。

      有一篇文章提到,可以把yield看成是生成器的return的一部分,首先一个return的作用是在程序中返回某个值,返回之后程序就不再往下执行了,那么生成器是是什么,只有调用next()方法的时候该函数才会执行。结合来看,当一个函数带有yield,它已经不是一个函数了,而是一个生成器,即一个返回迭代器的函数,返回的迭代器有一个next方法,但是每次走一步,到yield停一次,下一次执行到next()时,再从上一次暂停的位置开始,直到执行到下一个yield表达式,将yield关键字后的表达式列表返回给调用者,并再次暂停。

      举个例子:

    def run():
        print("starting...")
        while True:
            res = yield 1
            print("res:",res)
    
    g = run()
    print(next(g))
    print("------")
    print(next(g))
    
    输入结果如下:
    starting...
    1
    ------
    res: None
    1

      上面的表达有点绕口,但是如果你懂迭代器,那么在这里你就懂了。如果不懂,也没关系,我将一下迭代器。

      迭代器的功能主要用于访问集合元素,迭代器从集合的第一个元素开始访问,知道所有元素被访问完结束。向我们的list,tuple,string,dict,都是可以迭代的,对于我们自己实现的类型,如果实现了__iter__()或者__getitem__()方法,那么该类对象也是可以迭代的。

      抽象来看的话,迭代器是一个数据流,对迭代器不断调用next()方法,就可以依次获取下一个元素,当迭代器没有元素的时候,调用next()会抛出StopIteration异常。iter()方法则返回一个特殊的迭代对象,当出现StopIteration异常的时候,则识别迭代完成结束。最常见的就是我们的for循环:

    for i in range li:
        print(i)

      Python 处理for 循环时,首先会调用内建函数 iter(li),它实际上会调用 li.__iter__(),返回 li对应的迭代器。而后,li循环会调用内建函数 next(),作用在迭代器上,获取迭代器的下一个元素,并赋值给 x。这个时候,Python 才算是真正开始执行循环体。

      那问题来了,我们知道迭代器的用法,那生成器在什么地方可以用呢。那就不得不提到协程了。协程也叫微线程,举个例子:

      函数的调用都是层级调用,抽象来看是实现了栈的调用,a调用b,b调用c,那么c执行完毕返回,再b执行完毕返回,再a执行完毕。这里的调用顺序是明确的的。

      但是协程不同,虽然它也是子程序,但是在执行的过程中,子程序内部会发生中断,转而去执行别的子程序,在适当的时候再跳回来。

      那好处在哪里呢,线程的切换是需要开销的,而子程序的切换由程序自己控制,性能优势就有了。另外一个是因为只有一个线程,不存在读写冲突,在控制共享资源的时候不加锁,优势就更大了。

      正如前面所说的,yield能提供一个函数执行过程中的暂停,这个协程的子程序内部中断的思想不谋而合,如果使用协程去写生产者-消费者模型,那么当生产者生产消息以后,直接通过yield跳转到消费者开始执行,待消费者执行完毕以后,切换回生产者继续生产,效率极高:

    import time
    
    def consumer():
        r = ''
        while True:
            n = yield r
            if not n:
                return
            print('[CONSUMER] Consuming %s...' % n)
            time.sleep(1)
            r = '200 OK'
    
    def produce(c):
        next(c) #廖雪峰这里是2.7版本,已经改为3.0版本
        n = 0
        while n < 5:
            n = n + 1
            print('[PRODUCER] Producing %s...' % n)
            r = c.send(n)
            print('[PRODUCER] Consumer return: %s' % r)
        c.close()
    
    if __name__=='__main__':
        c = consumer()
        produce(c)

    参考:

    廖雪峰讲协程

    https://www.liaoxuefeng.com/wiki/897692888725344/923057403198272

    python中yield的用法详解——最简单,最清晰的解释

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

    Python3 迭代器与生成器

    https://www.runoob.com/python3/python3-iterator-generator.html

    Python 中的黑暗角落(一):理解 yield 关键字

    https://liam.page/2017/06/30/understanding-yield-in-python/

    什么是协程 ?

    https://juejin.im/post/5d5df6b35188252ae10bdf42

  • 相关阅读:
    static 关键字
    gitlab 配置到jenkins
    Eclipse下代码字体背景变红/变绿/变黄原因
    构造方法(和python初始化变量类似)
    面向对象(实际就像python跳用自己写的库那样)
    Python 的AES加密与解密
    break 和 continue 的用法
    for循环
    Eclipse快捷键
    java三元运算符
  • 原文地址:https://www.cnblogs.com/guangluwutu/p/13390352.html
Copyright © 2011-2022 走看看