[Python-Dev] Coroutines

Tim Peters tim_one at email.msn.com
Sat May 22 06:22:50 CEST 1999


[Tim]
> OK.  So how do you feel about coroutines?  Would sure be nice
> to have *some* way to get pseudo-parallel semantics regardless of OS.

[David Ascher]
> I read about coroutines years ago on c.l.py, but I admit I forgot it all.
> Can you explain them briefly in pseudo-python?

How about real Python?  http://www.python.org/tim_one/000169.html contains a
complete coroutine implementation using threads under the covers (& exactly
5 years old tomorrow <wink>).  If I were to do it over again, I'd use a
different object interface (making coroutines objects in their own right
instead of funneling everything through a "coroutine controller" object),
but the ideas are the same in every coroutine language.  The post contains
several executable examples, from simple to "literature standard".

I had forgotten all about this:  it contains solutions to the same "compare
tree fringes" problem Sam mentioned, *and* the generator-based building
block I posted three other solutions for in this thread.  That last looks
like:

# fringe visits a nested list in inorder, and detaches for each non-list
# element; raises EarlyExit after the list is exhausted
def fringe( co, list ):
    for x in list:
        if type(x) is type([]):
            fringe(co, x)
        else:
            co.detach(x)

def printinorder( list ):
    co = Coroutine()
    f = co.create(fringe, co, list)
    try:
        while 1:
            print co.tran(f),
    except EarlyExit:
        pass
    print

printinorder([1,2,3])  # 1 2 3
printinorder([[[[1,[2]]],3]]) # ditto
x = [0, 1, [2, [3]], [4,5], [[[6]]] ]
printinorder(x) # 0 1 2 3 4 5 6

Generators are really "half a coroutine", so this doesn't show the full
power (other examples in the post do).  co.detach is a special way to deal
with this asymmetry.  In the general case you use co.tran all the time,
where (see the post for more info)

    v = co.tran(c [, w])

means "resume coroutine c from the place it last did a co.tran, optionally
passing it the value w, and when somebody does a co.tran back to *me*,
resume me right here, binding v to the value *they* pass to co.tran ).

Knuth complains several times that it's very hard to come up with a
coroutine example that's both simple and clear <0.5 wink>.  In a nutshell,
coroutines don't have a "caller/callee" relationship, they have "we're all
equal partners" relationship, where any coroutine is free to resume any
other one where it left off.  It's no coincidence that making coroutines
easy to use was pioneered by simulation languages!  Just try simulating a
marriage where one partner is the master and the other a slave <wink>.

i-may-be-a-bachelor-but-i-have-eyes-ly y'rs  - tim






More information about the Python-Dev mailing list