Generator send() for Two-Way Communication

RoadmapsPython

Overview

The `.send()` method allows passing data back IN to a generator. Instead of just yielding values out, `yield` becomes an expression that evaluates to the value sent in. Think of generators as mini-programs paused at `yield`. Calling `.send()` resumes them and hands them a value. This two-way communication is the foundation of coroutines and asynchronous programming in Python. Generators provide three methods for two-way communication: - `.send(value)`: Resumes execution and passes `value` into the `yield` expression. Returns the next yielded value, or raises `StopIteration` if the generator exits. - `.throw(type)`: Raises an exception of the given `type` at the paused `yield` expression. Returns the next yielded value, or raises `StopIteration` if it catches the exception and exits. - `.close()`: Raises a `GeneratorExit` at the paused `yield`. Returns `None`, and forces the generator to exit. You must prime a generator before sending a value. You cannot send a non-`None` value to a newly started generator, because it has not yet reached a `yield` expression to receive it. Doing so raises a `TypeError`. def greeter(): name = yield print(f"Hello {name}") g = greeter() g.send("Alice") # TypeError: can't send non-None value to a just-started generator This edge case is covered fully in `coroutines`.

It transforms generators from simple data producers (one-way) into stateful coroutines that can receive data, process it, and respond (two-way).

Where used: `asyncio` event loops, custom state machines, data streaming pipelines

Why learn this

Code walkthrough

def greeter():
  print('Starting')
  while True:
    name = yield
    print(f'Hello {name}')

g = greeter()
next(g)
g.send('Alice')
g.send('Bob')

Focus: name = yield

Aha moment

def echo():
  received = yield 'Started'
  yield f'You said: {received}'

e = echo()
print(next(e))
print(e.send('Python'))

Prediction: What does the second print output?

Common guess: You said: Started

The `yield` evaluates to the value sent IN (`'Python'`), which is assigned to `received`, and then the generator yields the formatted string back OUT.

Common mistakes

Glossary

coroutines
Special functions that can pause and resume their execution, allowing multiple tasks to run cooperatively. Example: `name = yield` inside a generator makes it a coroutine that can receive values via `.send()`.
prime
To prepare a generator or coroutine for its first use by advancing it to its initial pause point. Example: `next(gen)` or `gen.send(None)` primes the generator before you call `.send(value)`.

Recall questions

Understanding checks

Why does calling `.send(5)` on a newly created generator raise a `TypeError`?

Because the generator hasn't reached a `yield` statement yet to receive the value.

A generator must be "primed" by advancing it to the first `yield` expression (usually by calling `next(gen)` or `gen.send(None)`) before it can receive data via `.send()`.

What is the output of `print(g.send('B'))`?

`'B'`

The first `next(g)` runs until the first `yield` and outputs `'A'`. Then `g.send('B')` resumes execution, assigning `'B'` to `val`, and immediately yields `val`.

Practice tasks

Running Average Generator

Complete the `running_average()` generator so that it receives numbers via `.send()` and yields the current average of all numbers received so far. The generator must yield `0.0` upon initialization.

Challenge

Stateful Filter Coroutine

Write a generator `threshold_filter(limit)` that receives numbers. If the received number is greater than `limit`, it yields the number. Otherwise, it yields `None`. Remember to handle the first `yield` which is needed just to prime the generator.

Return to Python Roadmap