【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