• python---await/async关键字


    推文:玩转 Python 3.5 的 await/async

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

    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 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 代码将其包起来,这样就可以避免错误提示。

    上面虽然将两个协程都执行起来了,但是并没有做到交替执行,和最初的并无不同,而且似乎更加麻烦

    协程与线程相似的地方是多个线程之间也可以交替执行,不过与线程不同之处在于协程之间的切换是显式的,而线程是隐式的(大多数情况下是更好的方式)。所以我们需要加入显式切换的代码。

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

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

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

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

    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 异常。

    为此我们

    新建一个函数,这个函数传入一个协程列表,函数执行这些协程直到全部结束。我们现在要做的就是调用这个函数。

    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])
  • 相关阅读:
    MySQL索引的操作
    MySQL表的操作02
    MySQL表的操作01
    字典实现简单购物车程序
    python 中if和elif的区别
    格式化操作---%方法
    正则表达式相关知识
    实现 像网易云音乐 播放列表那样的弹出型Dialog
    为什么在非UI线程中操作UI的改变失不安全的
    模板方法模式-Template Method
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9219257.html
Copyright © 2020-2023  润新知