python生成器和使用gevent操作协程

python中的生成器是一种特殊的迭代器,可以类比成类:

如下定义斐波那契数列,初始化obj1,相当于new 出一个对象

以下两种方式输出结果相同,均为斐波那契数列前10项

def fibonacci(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a
        a, b = b, a + b
        current_num += 1
obj1 = fibonacci(10)
for ind in range(10):
    print(next(obj1))
def fibonacci(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a
        a, b = b, a + b
        current_num += 1
obj1 = fibonacci(10)
for obj in obj1:
    print(obj)

其中yield关键字说明此函数为一个生成器,yield a可以作为一个返回值打断函数并返回结果,此为协程的雏形,如果定义两个生成器实例,两个生成器通过yield可以实现交替运行,即为协程

看如下代码:

def fibonacci(all_num,name):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a
        print(name)
        a, b = b, a + b
        current_num += 1
obj1 = fibonacci(10,"fib1")
obj2 = fibonacci(10,"fib2")
for ind in range(10):
    print(next(obj1))
    print(next(obj2))

输出:

这便实现了协程交替工作

使用greenlet管理协程:

使用greenlet的switch()方法替换yield

from greenlet import greenlet

def wh2():
    while True:
        print("---2---")
        gr1.switch()
        time.sleep(0.1)


def wh1():
    while True:
        print("---1---")
        gr2.switch()
        time.sleep(0.1)


gr1 = greenlet(wh1)
gr2 = greenlet(wh2)
gr1.switch()

 

输出如下:

使用最多最广泛的还是使用gevent管理协程:

安装gevent

pip install gevent

def wh2():
while True:
print("---2---")
time.sleep(0.1)


def wh1():
while True:
print("---1---")
time.sleep(0.1)

def spawn():
g1 = gevent.spawn(wh1)
g2 = gevent.spawn(wh2)
g1.join()
g2.join()

spawn()

直接代码中去掉yield、switch,使用gevent.spawn()方法---下蛋

但是输出如下:

 

原因是我们需要把time.sleep()替换成gevent.sleep(),join也可以替换成joinall

def wh2():
    while True:
        print("---2---")
        gevent.sleep(0.1)


def wh1():
    while True:
        print("---1---")
        gevent.sleep(0.1)

def spawn():
    g1 = gevent.spawn(wh1)
    g2 = gevent.spawn(wh2)
    gevent.joinall([g1,g2])

spawn()

如果不替换time.sleep(),我们也可以使用gevent的money模块全局替换:

from gevent import monkey

monkey.patch_all()

输出如下:

实现了协程工作,协程的开销是比线程还要小的,以上代码的sleep部分实际中可以去掉,因为在耗时部分gevent才会走其他的实例,为了方便测试才加上的,不然会直接不卡顿的for循环其中一个实例,如下的例子就是没有sleep代码的协程工作例子,由于网络通信比较耗时,所以协程会自动切换到另一个实例,好像是并发工作一样。

 

协程demo:

import gevent
from gevent import monkey
import urllib.request
# 补丁 实现协同工作:看上去就像是多线程 monkey.patch_all() def download_img(name,url): req
= urllib.request.urlopen(url) img_content = req.read() with open(name,"wb") as f: f.write(img_content) if __name__ == '__main__': gevent.joinall( [ gevent.spawn(download_img,"1.jpg","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_match%2F0%2F12070081997%2F0.jpg&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1627992970&t=0a1f9c49767613a1f8ec792c975c3ae6"), gevent.spawn(download_img,"2.jpg","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_match%2F0%2F6311385436%2F0.jpg&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1627992675&t=08d6f5ec50eb7570be0350512a67caea") ] )

 

posted @ 2021-07-04 17:07  陈扬天  阅读(148)  评论(0编辑  收藏  举报