python---await/async关键字

推文:玩转 Python 3.5 的 await/asyncpython

首先看正常的两个函数之间的执行

def func1():
    print("func1 start")
    print("func1 end")

def func2():
    print("func2 start")
    print("func2 a")
    print("func2 b")
    print("func2 c")
    print("func2 end")

func1()
func2()
func1 start
func1 end
func2 start
func2 a
func2 b
func2 c
func2 end

没法实现两个函数之间的交互。async

将这两个函数设置为协程,加上async关键字

async def func1():
    print("func1 start")
    print("func1 end")

async def func2():
    print("func2 start")
    print("func2 a")
    print("func2 b")
    print("func2 c")
    print("func2 end")

f1 = func1()
f2 = func2()
print(f1,f2)
<coroutine object func1 at 0x000000000084C468> <coroutine object func2 at 0x000000000084CD58>
sys:1: RuntimeWarning: coroutine 'func1' was never awaited  #警告信息
sys:1: RuntimeWarning: coroutine 'func2' was never awaited

#上面再也不只是函数,而变为协程对象,协程在调用时,不会被执行

那么,为何要有一个协程对象?代码到底如何执行?

关键之处是协程确实是与 Python 的生成器很是类似,也都有一个 send 方法。咱们能够经过调用 send 方法来启动一个协程的执行。函数

f1 = func1()
f2 = func2()

try:
    print('f1.send')
    f1.send(None)
except StopIteration as e:  #这里也是须要去捕获StopIteration方法
    pass

try:
    print('f1.send')
    f2.send(None)
except StopIteration as e:
    pass

StopIteration 异常是一种标记生成器(或者像这里的协程)执行结束的机制。虽然这是一个异常,可是确实是咱们指望的!咱们能够用适当的 try-catch 代码将其包起来,这样就能够避免错误提示。spa

上面虽然将两个协程都执行起来了,可是并无作到交替执行,和最初的并没有不一样,并且彷佛更加麻烦

协程与线程类似的地方是多个线程之间也能够交替执行,不过与线程不一样之处在于协程之间的切换是显式的,而线程是隐式的(大多数状况下是更好的方式)。因此咱们须要加入显式切换的代码。.net

咱们可能根据生成器或者协程gevent的使用,而去推测这种方案:(是错误的线程

async def func1():
    print("func1 start")
    yield 
    print("func1 end")

不容许在本地协程函数中使用yield,可是做为替换,咱们可使用到await 表达式来暂停协程的执行。注意await _something_,这里的_something_表明的是基于生成器的协程对象,或者一个特殊的相似 Future 的对象。code

下面使用一个基于生成器的协程函数

import types

@types.coroutine
def switch():
    yield

async def func1():
    print("func1 start")
    await switch()
    print("func1 end")

async def func2():
    print("func2 start")
    print("func2 a")
    print("func2 b")
    print("func2 c")
    print("func2 end")

f1 = func1()
f2 = func2()

try:
    f1.send(None)
except StopIteration as e:
    pass

try:
    f2.send(None)
except StopIteration as e:
    pass
func1 start
func2 start
func2 a
func2 b
func2 c
func2 end

注意上面的输出:只是执行了函数的转换,有func1转换到func2,并无再回到func1中。那么如何让其在回到func1中呢。协程

咱们能够基于生成器同样再次对func1协程使用send方法,再次激活yield向下执行

import types

@types.coroutine
def switch():
    yield

async def func1():
    print("func1 start")
    await switch()
    print("func1 end")

async def func2():
    print("func2 start")
    print("func2 a")
    print("func2 b")
    print("func2 c")
    print("func2 end")

f1 = func1()
f2 = func2()

try:
    f1.send(None)
except StopIteration as e:
    pass

try:
    f2.send(None)
except StopIteration as e:
    pass

try:
    f1.send(None)
except StopIteration as e:
    pass
func1 start
func2 start
func2 a
func2 b
func2 c
func2 end
func1 end

上面能够实现两个函数交替一次,回到原协程中。可是咱们更加但愿接二连三的调用send驱动不一样的协程去执行,直到send抛出 StopIteration 异常。对象

为此咱们blog

新建一个函数,这个函数传入一个协程列表,函数执行这些协程直到所有结束。咱们如今要作的就是调用这个函数。

import types

@types.coroutine
def switch():
    yield

async def func1():
    print("func1 start")
    await switch()
    print("func1 e")
    await switch()
    print("func1 end")

async def func2():
    print("func2 start")
    print("func2 a")
    await switch()
    print("func2 b")
    print("func2 c")
    await switch()
    print("func2 end")

def run(task_list):
    coro_list = list(task_list)

    while coro_list:
        for coro in list(coro_list):
            try:
                coro.send(None)
            except StopIteration:
                coro_list.remove(coro)

f1 = func1()
f2 = func2()

run([f1,f2])
相关文章
相关标签/搜索