Async Await in Python3

发布于 2018-04-11 17:48:18

History

  1. generators found in Python 3.3
  2. event loop in the form of asyncio: In Python 3.4, the asyncio.coroutine decorator was used to label a function as acting as a coroutine that was meant for use with asyncio and its event loop.
  3. yield from to await in Python 3.5

Event loop and task

Asynchronous Generators

A Python generator is any function containing one or more yield expressions:

def func():            # a function
    return

def genfunc():         # a generator function
    yield

We propose to use the same approach to define asynchronous generators:

async def coro():      # a coroutine function
    await smth()

async def asyncgen():  # an asynchronous generator function
    await smth()
    yield 42

The result of calling an asynchronous generator function is an asynchronous generator object, which implements the asynchronous iteration protocol defined in PEP 492.

It is a SyntaxError to have a non-empty return statement in an asynchronous generator.

Concepts

  • Generations: yield, yield from
  • Awaitable: used for await expression.
    • Future-like object: An object with an __await__ method
    • Coroutine object
      • Generator-based coroutines (function): @types.coroutine + yield
      • Native coroutines (function): async def + await + return value
  • Asynchronous context manager: An asynchronous context manager has __aenter__ and __aexit__ methods and can be used with async with.
  • Asynchronous iterable: An object with an __aiter__ method, which must return an asynchronous iterator object. Can be used with async for.
    • Asynchronous iterator: An asynchronous iterator has an __anext__ method

Key properties of coroutines:

  • async def functions are always coroutines, even if they do not contain await expressions.
  • It is a SyntaxError to have yield or yield from expressions in an async function.

Await expression

async def read_data(db):
    data = await db.fetch('SELECT ...')
    ...

await, similarly to yield from, suspends execution of read_data coroutine until db.fetch awaitable completes and returns the result data.

It uses the yield from implementation with an extra step of validating its argument.

await only accepts an awaitable, which can be one of:

  • A native coroutine object returned from a native coroutine function.
  • A generator-based coroutine object returned from a function decorated with types.coroutine().
  • An object with an __await__ method returning an iterator.
    • Any yield from chain of calls ends with a yield. This is a fundamental mechanism of how Futures are implemented. Since, internally, coroutines are a special kind of generators, every await is suspended by a yield somewhere down the chain of await calls
    • To enable this behavior for coroutines, a new magic method called __await__ is added. In asyncio, for instance, to enable Future objects in await statements, the only change is to add __await__ = __iter__ line to asyncio.Future class.
    • Objects with __await__ method are called Future-like objects in the rest of this PEP.
    • It is a TypeError if __await__ returns anything but an iterator.

Exceptions

  • It is a SyntaxError to use await outside of an async def function (like it is a SyntaxError to use yield outside of def function).
  • It is a TypeError to pass anything other than an awaitable object to an await expression.

Asynchronous context manager

An asynchronous context manager is a context manager that is able to suspend execution in its enter and exit methods.

To make this possible, a new protocol for asynchronous context managers is proposed. Two new magic methods are added: __aenter__ and __aexit__. Both must return an awaitable.

Call asynchronous context manager using async with.

Asynchronous iterators

An asynchronous iterable is able to call asynchronous code in its iter implementation, and asynchronous iterator can call asynchronous code in its next method. To support asynchronous iteration:

  • An object must implement an __aiter__ method returning an asynchronous iterator object.
  • An asynchronous iterator object must implement an __anext__ method returning an awaitable.
  • To stop iteration __anext__ must raise a StopAsyncIteration exception.
async for TARGET in ITER:
    BLOCK
else:
    BLOCK2
comments powered by Disqus