附加器

python3生成器yield,gre

发布时间:2022/11/7 14:05:39   
北京哪些皮肤科医院好 https://m-mip.39.net/disease/yldt/bjzkbdfyy/

生成器英文是gennerator,首先要明白,它的本质也是一种迭代器。

1.列表生成式

要创建一个生成器主要有两方法,最简单的一种是把列表生成式的[]换成(),这种用得少,方便用来进行简单的实验。如下(图一):

由上图可以看到[]里面存放的是结果,一个列表占用空间;()里面存放的是一个对象,生成数据的方式,不占用空间。

2.另一种是用gevent,对于比较复杂的算法和多任务,用列表生成式for循环无法实现时,我们用gevent。

gevent是对greenlet进行的封装,而greenlet又是对yield进行封装。要理解gevent就要从yield开始。

2.1yield

定一个斐波那契数列函数(图二):

定义一个生成器(图三):

运行结果一样:

比较这两段代码最大不同在于图中标记部分,图二是函数print输出,图三带yield关键字。像图二这种函数存在yield关键字的就不再是函数,而是一个生成器。生成器是一种特殊的迭代器,在调用它时,就不是调用函数,而是创建一个生成器对象。因此图三中a=fibonaccis(8)就是在创建一个生成器对象,然后就可以按照使用迭代器的方式来使用生成器了,再用forxina对这个可迭代对象遍历输出。而图二就是直接调用函数输出。生成器相比于函数,生成器可以暂停,占用很小的空间记录下下当前的数据,下次执行时继续执行。

在代码执行到yield关键字这里时会暂停,把yield关键字后面的值返还给生成器对象,也就是将迭代器中每次迭代返回数值的return换成了yield。再次唤醒取值时继续执行(函数是从头开始执行,生成器继续暂停的位置开始执行),直到遍历结束,生成器也是一个迭代器,可以用next()方法来调唤醒它继续执行(图四):

执行结果:

生成器的主要唤醒方法不仅可以使用next(),还可以使用send(),send()区别于next()的是send可以传一个附加值进去(图五)(当然我们也可以用__next__()等方法来唤醒,但不常用)。send(XX)如果需要传一个真实的值,是不能第一个调用的,因为第一次调用时从第一行开始执行,不是在yield处。第一次调用生成器用send(None)来实现。

执行结果:

3次调用fibt的值输出了2次,可看出先执行的是yield给生成器对象一个值进入暂定,再次唤醒的时候才继续运行,赋值给fibt这个变量。

使用for循环调用生成器时,如果生成器中有return语句的返回值,是拿不到这个返回值的。要拿到return的返回值就必须捕获StopIteration这个错误,因为返回值包含在StopIteration的value中。我们把图四改一下:

执行结果:

这样就拿到return的返回值了。

上面说了这么多都是一个基础,我们的目的是用它来实现python多任务--也就是协程。要明白什么是协程,我们要先明白并行和并发,并行是指我的CPU有多个内核,每一个任务占一个核,一起执行;只有一个核,多个任务在一个核里面交替执行,这种叫并发。python多任务实际就是并发。进程,线程,协程都能实现多任务,进程在切换进程时(需要加载代码,释放和生成内存空间等)占用的内存空间相当大,线程比进程好一点,生成器存储的是生成数据的代码,占用空间很小,这种实现多任务的方式就是协程。

写一个yield的多线程(图五):

执行结果:

在这里我们用yield是先多线程是用next()来控制和调度。

greenlet是对yield的封装,使用起来更方便。在使用greenlet时,我们需要用pip3installgreenlet先安装greenlet,我们用greenlet来实现一下图五的代码(图六):

运行结果:

这里创建了两个greenlet对象:cor1,cor2。分别对应函数coroutine_yie_en,coroutine_yie_da。使用greenlet的switch()方法进行协程切换。我们先调用cor1.switch()后函数coroutine_yie_en执行,在这个函数中又调用了cor2.switch,函数coroutine_yie_da执行,这样来回切换执行,当执行到3后调用cor1.swtich,函数coroutine_yie_en已经执行完了,不再调用cor2.switch,不会输出4。

greenlet实际用的也不多,实际应用中gevent,它一个并发库,是对greenlet的封装。我在用greenlet,yield进行协程切换的时候都是人工写好执行的,而gevent可以实现自动切换,其原理就是我们在用greenlet遇到IO等延时操作时(前面用到的sleep就是一个强制延时),自动切换到其它greenlet,等IO操作结束时,适当的时候再切回来。在使用gevent时,先安装pip3installgevent。写一个gevent多任务(图七):

运行结果:

用gevent实现多任务,延时类操作一般使用gevent来调用(例如:gevent.sleep(0.2)等)。如果在开发中我们很多地方不是这样操作的,这时候我们可以导入monkey类,用monkey.patch_all()来自动切换为gevent。生成器对象也写在一个joinall列表中(图八):

运行效果一样。



转载请注明:http://www.aideyishus.com/lkjg/2206.html
------分隔线----------------------------