Asynchronous programming

Steven D'Aprano steve+python at pearwood.info
Thu Aug 11 10:55:15 EDT 2016


On Thu, 11 Aug 2016 02:41 pm, Chris Angelico wrote:

> Consider these three ways of doing a database transaction:
> 
> def synchronous(id):
>     trn = conn.begin_transaction()
>     trn.execute("select name from people where id=%d", (id,))
>     name, = trn.fetchone()
>     trn.execute("update people set last_seen=now() where id=%d", (id,))
>     trn.commit()
>     return name

That makes perfect sense. Good old fashioned synchronous programming.


> def callbacks_1(cb, id):
>     conn.begin_transaction(callbacks_2, cb, id)
> def callbacks_2(trn, cb, id):
>     trn.execute("select name from people where id=%d", (id,),
>                 callbacks_3, cb, id)
> def callbacks_3(trn, cb, id):
>     trn.fetchone(callbacks_4, cb, id)
> def callbacks_4(trn, data, cb, id):
>     name, = data
>     trn.execute("update people set last_seen=now() where id=%d",
>                 (id,), callbacks_5, cb, name)
> def callbacks_5(trn, cb, name):
>     trn.commit(callbacks_6, cb, name)
> def callbacks_6(trn, cb, name):
>     cb(name)

Now you're surely pulling my leg. Your conn.begin_transaction has a
completely different signature! (No arguments in the first case, three in
this case.)


> def asynchronous(id):
>     trn = yield from conn.begin_transaction()
>     yield from trn.execute("select name from people where id=%d", (id,))
>     name, = yield from trn.fetchone()
>     yield from trn.execute("update people set last_seen=now() where
>                            id=%d", (id,))
>     yield from trn.commit()
>     return name

That ... looks wrong. You're taking something which looks like a procedure
in the first case (trn.execute), so it probably returns None, and yielding
over it. Even it that's not wrong, and it actually returned something which
you ignored in the first case, it looks like you're mixing two distinct
ways of using generators:

- Generator as iterator ("yield x" or "yield from subiterator"); 
  something which *sends* values out for the purpose of iteration.

- Generator as coroutine ("y = yield x"); something which *receives*
  values from the called using the send() method.

Another way of putting it is, "a bogus generator that generates and receives
values (not a recommended coding style)". (David Beazly, per url below.)

When I say coroutine here, I mean in the *old* sense, as described by David
Beasley's A Curious Course on Coroutines and Concurrency:

http://www.dabeaz.com/coroutines/

So there's something deeply suspicious to me about the coding style.




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list