[Python-Dev] Importance of "async" keyword

Nick Coghlan ncoghlan at gmail.com
Mon Jul 6 03:41:45 CEST 2015

On 6 July 2015 at 10:27, Chris Angelico <rosuav at gmail.com> wrote:
> On Mon, Jul 6, 2015 at 7:50 AM, Sven R. Kunze <srkunze at mail.de> wrote:
>> Seems like we stick to this example once again. So, let me get this
>> straight:
>> 1) I can add, subtract, multiply and divide real numbers.
>> 2) I can add, subtract, multiply and divide complex numbers.
>> 3) I can even add, subtract, multiply and divide complex numbers AND real
>> numbers altogether in a single expression.
>> 4) Granted, complex numbers can do more but that basically follows from
>> their definition and does not jeopardize ease of usage.
> Until you run into a TypeError: unorderable types: complex() >
> complex(), at which point you realize that they aren't a simple
> superset of reals with all the same operations supported.

Exactly. While complex numbers are a superset of the real numbers from
a value perspective, from a behavioural perspective, there are things
you can do when working only with real numbers that you can't do when
you have to account for the fact that here might be complex numbers
are in the mix. In a Python context, the essential operations specific
to real numbers are listed at

There's also a difference between the scope of the math and cmath modules:

    >>> import math, cmath
    >>> set(dir(math)) - set(dir(cmath))
    {'floor', 'pow', 'erf', 'trunc', 'copysign', 'expm1', 'ldexp',
'fsum', 'erfc', 'lgamma', 'frexp', 'gamma', 'factorial', 'log2',
'fabs', 'log1p', 'atan2', 'hypot', 'modf', 'radians', 'degrees',
'fmod', 'ceil'}

It's a general principle of design that expanding the kinds of values
you accept (e.g. from real numbers to complex numbers) means you
reduce the kinds of operations you can assume will work (e.g. to the
behaviours of 2D complex numbers, rather than the 1D real number
line). Similarly, as you step further down the numeric tower from real
numbers to rationals and integers, you reduce the set of supported
values, but you also increase the number of defined behaviours.

When it comes to coroutines and subroutines, coroutines are the
superset - a subroutine is just a coroutine that never suspends before
producing a result. You can thus make certain simplifying assumptions
when executing subroutines that you can't make when executing a

It also turns out that "never suspends" is actually problematic, which
is why nominally subroutine based code these days tends to instead
have *implicit* suspension points based on either operating system
level pre-emptive multithreading where suspension may occur at any
point or greenlet style state switching where suspension may occur as
part of any function call (whether an explicit call or as part of a
syntactic protocol). These approaches provide parallel execution at
the expense of the ability to reason locally about code correctness,
which then causes all sorts of *other* problems.

That said, I think there's definitely value in providing a very simple
answer to the "how do I make a blocking call from a coroutine?"
question, so I filed an RFE to add asyncio.blocking_call:

I'm less convinced of the value of "asyncio.wait_for_result()", so I
haven't filed an RFE for that one.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-Dev mailing list