美文网首页Python
Python Async/Await

Python Async/Await

作者: hehehehe | 来源:发表于2021-09-08 15:43 被阅读0次

    https://zhuanlan.zhihu.com/p/27258289
    https://docs.python.org/zh-cn/3/library/asyncio-task.html#running-an-asyncio-program

    import asyncio
    
    @asyncio.coroutine
    def hello():
        print("Hello world!")
        # 异步调用asyncio.sleep(1):
        r = yield from asyncio.sleep(1)
        print("Hello again!")
    
    # 获取EventLoop:
    loop = asyncio.get_event_loop()
    # 执行coroutine
    loop.run_until_complete(hello())
    loop.close()
    
    @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了,因此可以实现并发执行。
    
    
    可迭代、迭代器、生成器

    可迭代的对象,很好理解,我们很熟悉的:字符串,list,dict,tuple,deque等

    import collections
    from collections.abc import Iterable, Iterator, Generator
    
    # 字符串
    astr = "XiaoMing"
    print("字符串:{}".format(astr))
    print(isinstance(astr, Iterable))
    print(isinstance(astr, Iterator))
    print(isinstance(astr, Generator))
    
    字符串:XiaoMing
    True
    False
    False
    

    这些可迭代对象都不是迭代器,也不是生成器。它们有一个共同点,就是它们都可以使用for来循环
    可迭代对象,是其内部实现了,iter 这个魔术方法。
    可以通过,dir()方法来查看是否有iter来判断一个变量是否是可迭代的。
    迭代器其实就只是多了一个函数而已。就是next(),我们可以不再使用for循环来间断获取元素值。而可以直接使用next()方法来实现。

    from collections.abc import Iterator
    
    aStr = 'abcd'  # 创建字符串,它是可迭代对象
    aIterator = iter(aStr)  # 通过iter(),将可迭代对象转换为一个迭代器
    print(isinstance(aIterator, Iterator))  # True
    next(aIterator)  # a
    next(aIterator)  # b
    next(aIterator)  # c
    next(aIterator)  # d
    

    迭代器,是其内部实现了,next 这个魔术方法。(Python3.x)
    可以通过,dir()方法来查看是否有next来判断一个变量是否是迭代器的。

    迭代器,是在可迭代的基础上,加了一个next()方法。
    生成器,则是在迭代器的基础上(可以用for循环,可以使用next()),再实现了yield
    yield 是什么东西呢,它相当于我们函数里的return。在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。正是由于这个机制,才使用生成器在Python编程中大放异彩。实现节省内存,实现异步编程。

    如何运行/激活生成器

    激活主要有两个方法

    • 使用next()
    • 使用generator.send(None)
    def mygen(n):
        now = 0
        while now < n:
            yield now
            now += 1
    
    if __name__ == '__main__':
        gen = mygen(4)
    
        # 通过交替执行,来说明这两种方法是等价的。
        print(gen.send(None))
        print(next(gen))
        print(gen.send(None))
        print(next(gen))
    
    生成器的执行状态
    • GEN_CREATED # 等待开始执行
    • GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
    • GEN_SUSPENDED # 在yield表达式处暂停
    • GEN_CLOSED # 执行结束
    yield from关键字

    yield from 将一个可迭代对象变成一个迭代器返回,也可以说,yield from关键字可以直接返回一个生成器

    >>> # yield from 将一个可迭代对象变成一个迭代器返回
    >>> def func2():
        lst = ['str', 'tuple', 'list', 'dict', 'set']
        yield from lst
    
    >>> gen2 = func2()
    >>> next(gen2)
    'str'
    >>> next(gen2)
    'tuple'
    >>> for i in gen2:
        print(i)
    
    从生成器过渡到协程:yield

    我们知道生成器为我们引入了暂停函数执行(yield)的功能。当有了暂停的功能之后,人们就想能不能在生成器暂停的时候向其发送一点东西(其实上面也有提及:send(None))
    协程通过使用 yield 暂停生成器,可以将程序的执行流程交给其他的子程序,从而实现不同子程序的之间的交替执行

    In [142]: def test():
         ...:     i=0
         ...:     while i<5:
         ...:         t = yield i
         ...:         print(t,i)
         ...:         i+=1
    In [148]: a=test()
    
    In [149]: a.send(None)
    Out[149]: 0
    
    In [150]: a.send("34")
    34 0
    Out[150]: 1
    
    * yield i 是将i return给外部调用程序。
    * t = yield 可以接收外部程序通过send()发送的信息,并赋值给t

    为什么python的yield第一次不能用send发送数据?
    https://www.zhihu.com/question/28105502
    因为最开始,gen()函数运行到yield i时,是从gen()把i的值抛回给调用者所以最开始send进去一个具体值,是几个意思?顺序不对啊!!现在人家并不是回到gen()生成器,而是第一次运行gen()函数时人家要从gen()那里出来啊send(var)是发生在回到gen()时的操作,而不是从gen()出来时的操作,这是需要明确差别的一点

    因为协程是一个线程执行,那怎么利用多核CPU呢?
    最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
    Python对协程的支持是通过generator实现的。

    asyncio模块

    https://www.cnblogs.com/51try-again/p/11074621.html
    asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
    用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型
    然后在coroutine内部用yield from调用另一个coroutine实现异步操作。
    asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

    coroutine+yield from

    import asyncio
     
    @asyncio.coroutine
    def hello():
        print("Nice to learn asyncio.coroutine!")
        # 异步调用asyncio.sleep(1):
        r = yield from asyncio.sleep(1)
        print("Nice to learn asyncio.coroutine again !")
     
     
    # 获取EventLoop:
    loop = asyncio.get_event_loop()
    # 执行coroutine
    loop.run_until_complete(hello())
    loop.close()
    
    Nice to learn asyncio.coroutine !
    Nice to learn asyncio.coroutine again !
    

    为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。
     请注意,async和 await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换:

    • 把@asyncio.coroutine替换为async;
    • 把yield from替换为await。

    执行多个任务

    import threading
    import asyncio
     
     
    async def hello():
        print('Hello Python! (%s)' % threading.currentThread())
        await asyncio.sleep(1)
        print('Hello Python again! (%s)' % threading.currentThread())
     
    loop = asyncio.get_event_loop()
    tasks = [hello(), hello()]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
    Hello Python! (<_MainThread(MainThread, started 4536)>)
    Hello Python! (<_MainThread(MainThread, started 4536)>)
    Hello Python again! (<_MainThread(MainThread, started 4536)>)
    Hello Python again! (<_MainThread(MainThread, started 4536)>)
    

    执行多个任务获取返回值

    import threading
    import asyncio
     
    async def hello(seq):
        print('Hello Python! (%s)' % threading.currentThread())
        await asyncio.sleep(1)
        print('Hello Python again! (%s)' % threading.currentThread())
        return "It's done", seq
     
    loop = asyncio.get_event_loop()
    task1 = loop.create_task(hello(2))
    task2 = loop.create_task(hello(1))
    task_list = [task1, task2]
    tasks = asyncio.wait(task_list)
    loop.run_until_complete(tasks)
     
    for t in task_list:
        print(t.result())
    

    可等待 对象有三种主要类型: 协程, 任务 和 Future.

    • 协程函数: 定义形式为 async def 的函数;

    • 协程对象: 调用 协程函数 所返回的对象。

    import asyncio
    
    async def nested():
        return 42
    
    async def main():
        # Schedule nested() to run soon concurrently
        # with "main()".
        task = asyncio.create_task(nested())
    
        # "task" can now be used to cancel "nested()", or
        # can simply be awaited to wait until it is complete:
        await task
    
    asyncio.run(main())
    

    相关文章

      网友评论

        本文标题:Python Async/Await

        本文链接:https://www.haomeiwen.com/subject/lbhfeltx.html