yield and send in generator: http://kissg.me/2016/04/09/python-generator-yield/
再来看一段yield
更复杂的用法,或许能加深你对generator
的next()
与send(value)
的理解.
>>> def echo(value=None):
... while 1:
... value = (yield value)
... print("The value is", value)
... if value:
... value += 1
...
>>> g = echo(1)
>>> next(g)
1
>>> g.send(2)
The value is 2
3
>>> g.send(5)
The value is 5
6
>>> next(g)
The value is None
上述代码既有yield value
的形式,又有value = yield
形式,看起来有点复杂.但以yield
分离代码进行解读,就不太难了.第一次调用next()
方法,执行到yield value
表达式,保存上下文环境暂停返回1
.第二次调用send(value)
方法,从value = yield
开始,打印,再次遇到yield value
暂停返回.后续的调用send(value)
或next()
都不外如是.
但是,这里就引出了另一个问题,yield
作为一个暂停恢复的点,代码从yield
处恢复,又在下一个yield
处暂停.可见,在一次next()
(非首次)或send(value)
调用过程中,实际上存在2个yield
,一个作为恢复点的yield
与一个作为暂停点的yield
.因此,也就有2个yield
表达式.send(value)
方法是将值传给恢复点yield
;调用next()
表达式的值时,其恢复点yield
的值总是为None
,而将暂停点的yield
表达式的值返回.为方便记忆,你可以将此处的恢复点记作当前的(current),而将暂停点记作下一次的(next),这样就与next()
方法匹配起来啦.
@asyncio.coroutine
把一个generator标记为coroutine类型,然后,我们就把这个coroutine
扔到EventLoop
中执行。
hello()
会首先打印出Hello world!
,然后,yield from
语法可以让我们方便地调用另一个generator
。由于asyncio.sleep()
也是一个coroutine
,所以线程不会等待asyncio.sleep()
,而是直接中断并执行下一个消息循环。当asyncio.sleep()
返回时,线程就可以从yield from
拿到返回值(此处是None
),然后接着执行下一行语句。
把asyncio.sleep(1)
看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,而是去执行EventLoop
中其他可以执行的coroutine
了,因此可以实现并发执行。
我们用Task封装两个coroutine
试试:
import threading
import asyncio
@asyncio.coroutine
def hello():
print('Hello world! (%s)' % threading.currentThread())
yield from asyncio.sleep(1)
print('Hello again! (%s)' % threading.currentThread())
loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
观察执行过程:
Hello world! (<_MainThread(MainThread, started 140735195337472)>)
Hello world! (<_MainThread(MainThread, started 140735195337472)>)
(暂停约1秒)
Hello again! (<_MainThread(MainThread, started 140735195337472)>)
Hello again! (<_MainThread(MainThread, started 140735195337472)>)
loop.run_until_complete中的task是按task投入的顺序串行执行,如果需要优先级调整task呢?