Coroutine is a class in its nature?

Viewed 376

I am following an instruction to learn coroutine

def grep(pattern):
    print("Looking for %s" % pattern)  # prime it(explain shortly)
    while True:
        line = (yield) # expression
        if pattern in line:
            print(line)

Test it

>>> g = grep("python")
>>> g.next()
Looking for python
>>> g.send("coroutine test")
>>> g.send("learning python")

It seems that a yield expression perform as a functools.partial, excluding it should be primed using next()

at this point, def grep is actually a class grep, because it initiate a generator object in first place.

The coroutine is tricky to follow, is my understanding on the right direction to continue without further side-effects since python named it def rather than class should has her reasons.

1 Answers

It seems that a yield expression perform as a functools.partial, [except that] it should be primed using next().

I'm not sure what specifically makes you say that, but I don't immediately see the parallels. functools.parital is designed to do parital binding of some args/kwargs to a callable and let you save some other args/kwargs to be called by the user. ("partial() is used for partial function application which 'freezes' some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.") That is not really what's going on with this generator, or any generator.

The coroutine is tricky to follow, is my understanding on the right direction to continue without further side-effects since Python named it def rather than class?

They are tricky, agree with you there. But I'm not sure if I'm seeing how a coroutine is "like a class in its nature." A coroutine is a specialized generator. Generators are defined with def and able to suspend and resume their execution. That describes generators, not classes, and just replacing def with class would be syntactically invalid, for starters.

One way you can think of any expression like a = yield b is to mark a break point.

When you call next(g), it will advance until it hits a yield statement, and stop there. It will push the resulting value to the calling stack but it will suspend its execution and stop there, being resumable when you again call next() on it. (This is the key difference between functions and generators, and between functions and coroutines, by extension.)

On the first call to next(), line is None. (Basically, line = yield None.) You will not be able to iterate over this because you can't say for pattern in None. What "priming" means in this case probably refers to the fact that an initial call to next(g) is analogous to g.send(None).

Now when you send additional values into the generator, they will be assigned to line, while pattern remains "python." If "python" is found in whatever you .send() in, it gets printed.

>>> g = grep("python")
>>> n = g.send(None)  # equiv to next(g); stop at line = (yield)
Looking for python
>>> n is None
True
>>> g.send("coroutine test")
>>> g.send("coroutine test")
>>> g.send("coroutine test")  # no match
>>> g.send("learning python") # match
learning python
>>> g.send("python3.7") # match
python3.7
Related