Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get return value from coroutine in python

I'm trying coroutines pipeline according to http://www.dabeaz.com/coroutines/Coroutines.pdf

The question is, how can I get value from the sink rather than only print it?

Take this code for example

def coroutine(func):
    def start(*args, **kwargs):
        cr = func(*args, **kwargs)
        next(cr)
        return cr
    return start


@coroutine
def produce(target):
    while True:
        n = (yield)
        target.send(n*10)


@coroutine
def sink():
    try:
        while True:
            n = (yield)
            print(n)
    except GeneratorExit:
        pass


sk = sink()
pipe = produce(sink())

With this code I get:

>>> pipe.send(10)
100

Then I want to get the return value rather than print it, I try to yield from sink:

@coroutine
def sink():
    try:
        while True:
            yield (yield)
    except GeneratorExit:
        pass

But it seems not working, pipe.send(10) still returns None rather than a generator.

So how shall I get the return value?

like image 539
lxyu Avatar asked Sep 04 '13 10:09

lxyu


People also ask

How does coroutine get return value?

How can I capture that value and return it when calling the method? You would need to make searchTeamEvents() a suspend function and reorganize your code to take advantage of that, so you can get rid of launch() from searchTeamEvents() and move it into whatever calls searchTeamEvents() .

How does coroutine work in Python?

Coroutines work cooperatively multitask by suspending and resuming at set points by the programmer. In Python, coroutines are similar to generators but with few extra methods and slight changes in how we use yield statements. Generators produce data for iteration while coroutines can also consume data.

How do you make a coroutine in Python?

Creating TasksWrap the coro coroutine into a Task and schedule its execution. Return the Task object. If name is not None , it is set as the name of the task using Task.set_name() . The task is executed in the loop returned by get_running_loop() , RuntimeError is raised if there is no running loop in current thread.

Is a generator a coroutine?

Coroutines and generators are very different concepts. Generators let you create functions that look like iterators to the consumer. Coroutines are an extension of the concept of traditional functions. A function will hand control back to its caller once through the return statement.


1 Answers

Why should pipe.send return a generator? And what are you going to do with the returned value?

Whatever it is, it should be done in sink.

You could, however, change your functions to

@coroutine
def produce(target):
    while True:
        n = (yield)
        yield target.send(n*10)

@coroutine
def sink():
    try:
        while True:
            yield (yield)
    except GeneratorExit:
        pass

to yield the value yielded by target, so pipe.send(10) will just return 100 instead of printing it.

But now you mix the producer and the consumer, which will potentially give you some headache.


In response to your comment:

from collections import defaultdict

def coroutine(func):
    def start(*args, **kwargs):
        cr = func(*args, **kwargs)
        next(cr)
        return cr
    return start

@coroutine
def produce(key, target):
    while True:
        n = (yield)
        target.send((key, n*10))

class Sink(object):

    def __init__(self):
        self.d = defaultdict(lambda: None)
        self.co = self.sink()

    def send(self, *args):
        self.co.send(*args)

    @coroutine
    def sink(self):
        try:
            while True:
                key, n = yield
                self.d[key] = max(self.d[key], n)
        except GeneratorExit:
            pass


sk = Sink()
pipeA = produce("A", sk)
pipeB = produce("B", sk)

pipeA.send(10)
pipeA.send(20)
pipeA.send(40)

pipeB.send(20)
pipeB.send(40)
pipeB.send(60)

print sk.d.items() # [('A', 400), ('B', 600)]
like image 88
sloth Avatar answered Sep 20 '22 23:09

sloth