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