yield结合装饰器(无返回值)
def deco(func):
def wrapper():
yie = func()
print(type(yie))
ret = next(yie)
return yie
return wrapper
@deco
def foo():
while True:
x = yield
print("x-->",x)
g = foo()
g.send(1)
g.send(2)
输出如下:
<class 'generator'>
x--> 1
x--> 2
以上代码的大概意思是:
@deco是装饰器的语法糖模式,foo()是执行deco函数中wrapper函数,yie = func()是返回一个generator生成器,然后next(yie),这里原因是g.send不能用在第一个触发生成器 ,然后在main线程里面,g = foo()返回一个生成器,然后g.send(1) g.send(2)就输出foo()函数的打印输出
yield结合装饰器(有返回值)
我们在来看一个yield带返回值的
print("--------------------")
def deco(func):
def wrapper():
res = func()
next(res)
return res
return wrapper
@deco
def foo():
food_list = []
while True:
food = yield food_list #返回添加food的列表
food_list.append(food)
print("elements in foodlist are:",food)
g = foo()
print(g.send('苹果'))
print(g.send('香蕉'))
re = g.send('菠萝')
输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
--------------------
elements in foodlist are: 苹果
['苹果']
elements in foodlist are: 香蕉
['苹果', '香蕉']
elements in foodlist are: 菠萝
Process finished with exit code 0
以上的代码g.send(‘xx”)返回一个food_list,然后输出列表信息即可
yield实现并发效果
以上的yield是跟装饰器结合使用,我们来看看yield能不能实现,程序遇到io实现切换?
以下的例子是生产者生产数据,消费者消费数据
#基于yield并发执行
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
print("consumer拿到数据了x:",x)
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(10):
print("producer生产数据了",i)
g.send(i)
start=time.time()
#基于yield保存状态,实现两个任务直接来回切换,即并发的效果
#PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
producer()
stop=time.time()
print(stop-start)
代码输出:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
producer生产数据了 0
consumer拿到数据了x: 0
producer生产数据了 1
consumer拿到数据了x: 1
producer生产数据了 2
consumer拿到数据了x: 2
producer生产数据了 3
consumer拿到数据了x: 3
producer生产数据了 4
consumer拿到数据了x: 4
producer生产数据了 5
consumer拿到数据了x: 5
producer生产数据了 6
consumer拿到数据了x: 6
producer生产数据了 7
consumer拿到数据了x: 7
producer生产数据了 8
consumer拿到数据了x: 8
producer生产数据了 9
consumer拿到数据了x: 9
0.0
Process finished with exit code 0
基于yield保存状态,实现两个任务直接来回切换,即并发的效果
PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
yield不能实现io切换
yield是实现了并发的效果,但是它能实现,在io阻塞,进行任务间切换么?我们来看一个例子
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
print("consumer拿到数据了x:", x)
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(3):
print("producer生产数据了")
g.send(i)
time.sleep(2)
start=time.time()
producer() #并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行
stop=time.time()
print(stop-start)
producer生产数据了
consumer拿到数据了x: 0
(有io阻塞)
producer生产数据了
consumer拿到数据了x: 1
(有io阻塞)
producer生产数据了
consumer拿到数据了x: 2
6.001317262649536
通过输出效果显示,并不能
greenlet任务切换
如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦(需要先得到初始化一次的生成器,然后再调用send。。。非常麻烦),而使用greenlet模块可以非常简单地实现这20个任务直接的切换
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。
单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度
from greenlet import greenlet
def eat(name):
print('%s eat 1' %name)
g2.switch('aaa')
print('%s eat 2' %name)
g2.switch()
def play(name):
print('%s play 1' %name)
g1.switch()
print('%s play 2' %name)
g1=greenlet(eat)
g2=greenlet(play)
g1.switch('safly')#可以在第一次switch时传入参数,以后都不需要
输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/python.py
safly eat 1
aaa play 1
safly eat 2
aaa play 2
Process finished with exit code 0