python yield 与 协程的实现

今天早上偶然看到一篇文章《PHP如何实现协程》,顿时惊呆了!PHP什么时候这么强了?那我还写Go 做什么?仔细阅读文章发现php使用的是yield关键实现的,心想这个不就是py里的生成器吗?虽然我不知道PHP里的yield和py的有什么区别,由于之前并没有深入了解过着玩意所以借机会了解一下!

我们都知道函数(子例程)的控制权要等到return 后才会交给调用者,函数中的变量随着控制权结束后也就消失了,需要等到下次调用重新开始,然而yield 就是这么神奇可以执行到yield 关键字处然后将控制权交给调用者,然后在次进入函数从上次结束的地方继续执行不断循环

先从一个小需求入手:

求出一个数字列表中的素数

import math
def get_primes(input_list):
    return (element for element in input_list if is_prime(element))


def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3,int(math.sqrt(number)+1),2):
            if number % current == 0:
                return False
        return True
    return False
nums  = [1,2,3,4,5,6,7,8,9]
list = get_primes(nums)
for i in list:
    print(i)

这个需求很简单就实现了~~·

假如现在万恶的产品经理想要求无限大的素数

那么你可能会想我直接从一个非常的大的数开始算不行么?可以的!但是就是有点LOW

如果我们往list存无限个数字 就更加不可能了

这是我们的yield就非常有作用了,上面说了遇到yield 会将交出控制权,然后进入函数继续执行

这里我们改造一下get_primes 函数


def get_primes_v2(number):
    while True:
        time.sleep(2)
        print("*******", number)
        if is_prime(number):
            yield number
        number+=1

使用while True不停的去执行并且每次到yield 关键字处就将结果返回并且保存函数的内部的状
态,实现每次number+1 的效果这就是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):
    c.__next__()
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print("send.......")
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()


c = consumer()
produce(c)

注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:

首先调用c.next()启动生成器;

然后,一旦生产了东西,通过c.send(n)切换到consumer执行;

consumer通过yield拿到消息,处理,又通过yield把结果传回;

produce拿到consumer处理的结果,继续生产下一条消息;

produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

其实通过yield 也知道go 中goroutine 翻译成协程还是有很大的不同的,goroutine 和协程是根本上不同的东西,之后再探讨go 中的goroutine 是如何实现的

猜你喜欢

转载自blog.csdn.net/raogeeg/article/details/84027951