On 06/26/2015 06:48 AM, Sven R. Kunze wrote:

> def business():
>      return complex_calc(5)
>
> def business_new()
>      return await complex_calc(10)

> Maybe, I completely missed the point of the proposal, but this is the way I 
> would expect it to work. Putting in an 'await' whenever I see fit and it just 
> works.

Assuming "async def business_new" (to avoid the syntax error), there's no 
difference between those functions or the one they're calling:

* "complex_calc" returns an awaitable object that, after you've awaited it, 
will result in an int.
* "business" returns the return value of "complex_calc", which is an awaitable 
object that, after you've awaited it, will result in an int.
* "business_new" returns an awaitable object that, after you've awaited it, 
will result in an int.

In all three of these cases, the result is the same. The fact that the 
awaitable object returned from any of them is implemented by a coroutine isn't 
important (in the same way that an iterable object may be implemented by a 
generator, but it's really irrelevant).

The value of the await syntax is when you're doing something interesting, 
a.k.a. your function is more than delegating directly to another function:

async def business_async():
    tasks = [complex_calc_async(i) for i in range(10)]
    results = [await t for t in tasks]
    await write_to_disk_async(filename, results)
    return sum(results)

Now it's actually useful to be able to await when we choose. Each call to 
complex_calc_async() could be starting a thread and then suspending until the 
thread is complete, so we actually start all 10 threads running here before 
blocking, and then collect the results in the order we started them (and not 
the order they complete, though I think asyncio has a function to do that). 
Without the explicit await this would be impossible.

The async def also lets us create coroutines consistently even if they don't 
await anything:

if has_version:
    async def get_version_async():
        return VERSION
else:
    async def get_version_async():
        return (await get_major_version_async(), await 
get_minor_version_async())

async def show_version():
    print(await get_version_async())

If, like generators, regular functions became coroutines only in the presence 
of an await, we'd have to do convoluted code to produce the fast 
get_version_async(), or else make the caller worry about whether they can skip 
the await. (Also consider calls that cache their result - a coroutine MUST be 
awaited, but it doesn't have to await anything if it already has the result).

(Aside: the "_async" suffix is based on the convention used in C#, and it's 
certainly one that I'll be using throughout my async Python code and 
encouraging in any code that I review. It's the most obvious way to let callers 
know whether they need to await the function result or not.)

Cheers,
Steve

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to