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 even 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 hitting batter both make it round. In this scenario, we can’t just increment the score by 1 each time; that’d be too simple for rounders!
So, let’s craft a generator that increments by 1/2 by default but can also handle custom increments when we need them. Here’s the magic:
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 star of the show—it’s what lets us use the send() method
alongside the usual next() function. When we call send(value), that value
gets passed to incr. When we use next(), yield score returns None.
That’s why, on line 6, we check if incr is None: if it is, we add the
default (0.5); otherwise, we add whatever was sent. Let’s see it in action:
>>> 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, while the Python Docs for
generator.send
gives the bare-bones rundown. There’s also an async
version,
with asend(), although this one has to be awaited.
Until next time, happy coding.
Paul