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") ] )