Python Generators, yield and send

Let’s say you’re programming a scoring system for the local rounders team. In rounders, a batter can score a half-rounder or a full-rounder. It’s also possible for one batter to score a half-rounder and another to score a full-rounder off the same ball, for example if a batter on base 3 and the batter hitting both make it round. In this scenario we can’t just increment the score by 1 each time.

So let’s make a generator that will increase by 1/2 each time by default, but will also accept other values to increment by.

def score_tally():
    score = 0
    default = 0.5
    while True:
        incr = yield score
        score += incr if incr is not None else default

Line 5 is the special one here, that’s what lets us use the send function, as well as the normal next function. When using send, the value is passed to incr. When using next, yield score will return None. This is why we check for None on line 6, before adding incr, or default if incr is None. We can then use this generator like so:

>>> bonkers_batters = score_tally()
>>> next(bonkers_batters)
0
>>> next(bonkers_batters)
0.5
>>> next(bonkers_batters)
1
>>> next(bonkers_batters)
1.5
>>> bonkers_batters.send(1)
2.5
>>> bonkers_batters.send(1.5)
4
>>> next(bonkers_batters)
4.5

The Python Docs for Yield mention send, and the Python Docs for generator gives the bare-bones description. There’s also an async version, although this one has to be awaited.

Until next time, happy coding. Paul