PEP 492: isn't the "await" redundant?

Chris Angelico rosuav at gmail.com
Fri Aug 26 05:18:24 EDT 2016


On Fri, Aug 26, 2016 at 6:55 PM, Kouli <dev at kou.li> wrote:
> recently, I have discovered Python's coroutines and enjoyed the whole
> asyncio system a lot. But I ask you to make me understand one thing in
> Python's coroutines design: why do we have to use "await" (or "yield
> from") in coroutines? Why can coroutines etc. not be used
> _from_coroutines_ (designated by 'async def') by a simple call-like
> syntax (i.e. without the 'await' keyword)?

Two reasons. One is that Python allows you to call any function and
inspect its return value - async functions are no different, and they
do return something. The other is that it makes yield points obvious.
Consider this hypothetical function:

async def get_user_data(id):
    await db.query("select name from users where id=?", (id,))
    name = await db.fetchone()[0]
    # transaction handling elided
    return name

Now, suppose we're trying to figure out what's going on. One good
solid technique is what I call "IIDPIO debugging": If In Doubt, Print
It Out.

async def get_user_data(id):
    print("Starting g_u_d")
    q = db.query("select name from users where id=?", (id,))
    print(q)
    await q
    f = db.fetchone()[0]
    print(f)
    name = await f
    # transaction handling still elided
    print("g_u_d: Returning %r" % name)
    return name

It's completely obvious, here, that this function will call db.query,
print stuff out, and then put itself on ice until the query's done,
before attempting the fetch. If the call on the second line
automatically put the function into waiting mode, this display would
be impossible, and the wait points would be entirely implicit. (If you
want implicit wait points, use threading, not async I/O.)

It's also possible to wait for things that didn't come from function
calls per se. For instance, the same database lookup could be
implemented using an ORM, something like this:

class Table:
    async def __getitem__(self, id):
        if id in self._cache:
            return self._cache[id]
        await db.query(...)
        data = await db.fetchone()
        self._cache[id] = self.make_object(...)
        return self._cache[id]

Whether this is good code or not is up to you, but it's perfectly
legal, and would be used as "users = Table(...); my_user = await
users[123]". To allow that, Python absolutely has to allow arbitrary
expressions to be waited on, not just function calls.

Does that answer the question?

ChrisA



More information about the Python-list mailing list