Search the courses, subjects, tracks, games and features, or jump straight to a page.
Learn it your way
A short class with Sarah and her students. The other ways are under More.
Sign up free to listen. Transcripts are free to read.
Today we’ll learn how generators can receive values while running. What method do you think allows us to send data into a generator?
Is it .send()?
Exactly! Unlike next(), which only resumes execution, .send(value) both resumes and sends data. But remember, before sending data, we must start the generator using next() or send(None). Let’s try a simple example together.
In received = yield 'Ready to receive', where does the value of received come from?
From the caller. The generator first hands out 'Ready to receive' and pauses. When we call gen.send('Hello'), the paused yield evaluates to 'Hello', and that is what is stored in received.
If you call .send() before the generator starts, what do you expect?
An error?
Right, a TypeError. We must prime the generator first. Also, sending values can let the generator change behavior on the fly, which is useful in many applications like interactive workflows or co-routines.
Is sending None an error too, before it starts?
No. send(None) is allowed and does exactly what next() does: it runs the generator up to its first yield. Only a non-None value is rejected.
Let's design a generator that keeps a running total. What should it yield each time?
The total so far, after each number we send in.
Good. Inside a loop we write value = yield total, then add value to total. The state lives in ordinary local variables that survive between sends.
And the caller just keeps calling send() with new numbers?
Exactly. Prime it once, then every send(number) returns the updated total. That is a tiny coroutine: it consumes data and produces results.
Generators support two-way communication using .send().
You must prime the generator before sending values.
yield expressions can both output and receive values.
This feature enables advanced control flows like coroutines and interactive generators.
gen.send(None) does exactly what next(gen) does.
Simple echo generator receiving and returning sent values. This version also counts the messages it has received:
def echo():
count = 0
reply = "Ready to receive"
while True:
received = yield reply
count += 1
reply = "Got message %d: %s" % (count, received.upper())
gen = echo()
print(next(gen))
print(gen.send("hello"))
print(gen.send("world"))Stateful generators changing behavior dynamically based on inputs via .send(). This thermostat reacts to each temperature reading, and a ("set", value) message changes its target:
def thermostat(target):
action = "ready"
while True:
message = yield action
if isinstance(message, tuple) and message[0] == "set":
target = message[1]
action = "target is now %d" % target
elif message < target - 1:
action = "heat"
elif message > target + 1:
action = "cool"
else:
action = "idle"
t = thermostat(21)
print(next(t))
for reading in [18, 21, 25]:
print(reading, "->", t.send(reading))
print(t.send(("set", 25)))
print(25, "->", t.send(25))Forgetting to prime raises a TypeError; send(None) primes the generator just like next():
def echo():
received = yield "Ready to receive"
while True:
received = yield f"Got: {received}"
gen = echo()
try:
gen.send("Hello")
except TypeError as e:
print("TypeError:", e)
print(gen.send(None))
print(gen.send("Hello"))A small decorator that primes a generator as soon as it is created, so callers can use send() straight away:
import functools
def primed(func):
@functools.wraps(func)
def start(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return start
@primed
def numbered():
n = 0
line = None
while True:
text = yield line
n += 1
line = "%d: %s" % (n, text)
log = numbered()
print(log.send("first message"))
print(log.send("second message"))"Send a value, wake the yield, two-way talk in Python’s field!"
Imagine a mailbox that not only delivers letters (yield) but also receives replies (send).
S.Y.N.C. — Send, Yield, Next (prime), Communicate two ways.
P.S.R. - Prime with next(), Send a value, Receive the next yield.
Term
What method sends data into a generator?
Click to flip
Term
How do you start a generator before sending values?
Click to flip
Term
What happens if you call `.send(value)` before the first yield?
Click to flip
Term
In `received = yield x`, what does `received` hold after the caller runs `gen.send(5)`?
Click to flip
Term
What does `gen.send(value)` return?
Click to flip
A method used to resume a generator and send a value into it at the paused yield.
The initial call to next() or send(None) to start generator execution up to the first yield.
A function that can pause and resume execution, exchanging data with the caller via yields and sends.
A function containing yield; calling it returns a generator object that runs the body step by step.
A yield used as a value, as in x = yield y: it sends y out and evaluates to whatever is sent back in.
A generator object that has been created but has not yet run to its first yield.
Generators can receive data sent into them during iteration using the .send() method, enabling dynamic interactions and control of the generator’s internal state.
This section covers how generators accept values via .send(value), which allows the caller to send data into the generator while it is paused at a yield statement. This enables two-way communication, making generators useful for coroutines, interactive data pipelines, and stateful computations.
Generators in Python can not only produce values to the caller but also receive values sent back to them. This feature allows two-way communication with a generator, enabling more flexible and interactive data flows. The key method for this is .send(value), which resumes the generator and sends a value to the paused yield expression inside it.
In addition to yielding values out, generators can receive values sent into them through the .send() method. This powerful mechanism allows external code to dynamically influence the generator’s behavior as it runs.
How .send() works:
yield expression, calling .send(value) resumes the generator and the yield expression evaluates to the sent value.next() or .send(None) to start execution up to the first yield. You cannot send non-None values before the generator starts.def echo():
received = yield "Ready to receive"
while True:
received = yield f"Got: {received}"
gen = echo()
print(next(gen)) # Start the generator, prints: Ready to receive
print(gen.send("Hello")) # Sends "Hello", prints: Got: Hello
print(gen.send("World")) # Sends "World", prints: Got: WorldThe generator yields a message, then waits to receive a value via send().
The received value is assigned to the received variable and can be used inside the generator’s logic.
Step by step: next(gen) runs the body up to the first yield and hands out "Ready to receive". gen.send("Hello") resumes it, so that first yield expression evaluates to "Hello", which is stored in received; the loop then reaches the next yield and hands out "Got: Hello". Each send() both delivers a value and returns the next value the generator yields.
next() or send(None) before sending other values.yield..send() is called before the first yield, a TypeError is raised..send(None) is equivalent to calling next() and is used to start the generator.To be precise about the third point: it is a non-None value that fails, with the message can't send non-None value to a just-started generator. Sending None is always allowed, which is exactly why send(None) can be used for priming.