Understanding yield in Python: the complete guide
The yield
keyword in Python allows you to create what we call a generator function.
Unlike return
which returns a value once and for all, yield
suspends the execution of the function and preserves its state to resume where it left off.
This little mechanism makes yield
ideal for:
- Iterating over large amounts of data;
- Creating lazy data streams (the famous lazy-loading);
- Improving memory performance.
In summary,
yield
transforms a classic function into a generator, capable of producing values on the fly.
Yield syntax and example
Yield syntax
The syntax of yield
is extremely simple:
def my_function():
yield value
It's used exactly like the return
keyword! 😉
Each time execution reaches the yield
keyword, it returns a value to the caller, but does not terminate the function. On the next call, it resumes right after the yield
.
A function containing at least one
yield
automatically becomes a generator.
Example of using yield
Let's take a small example to better understand how yield
works!
Imagine a pizzeria that prepares pizzas one by one. Each pizza is "yielded" as soon as it's ready, instead of waiting for all of them to be finished.
def make_pizzas():
yield "🍕 Margherita ready!"
yield "🍕 Queen ready!"
yield "🍕 4 Cheese ready!"
for pizza in make_pizzas():
print(f"Order sent: {pizza}")
Result:
Order sent: 🍕 Margherita ready!
Order sent: 🍕 Queen ready!
Order sent: 🍕 4 Cheese ready!
This type of approach is perfect for simulating queuing systems, data streaming, or batch production. Each yield
allows sending data as soon as it's available, without blocking the execution of the rest of the program.
Understanding generators in Python
Classic function vs generator: what are the differences?
A classic function executes its code, returns a value with return
and terminates immediately.
A generator function uses yield
. It:
- Can produce multiple values successively;
- Does not terminate its execution at each
yield
; - Resumes where it was interrupted.
Let's take this comparative example:
# Classic function
def classic():
return [1, 2, 3]
# Generator
def generator():
yield 1
yield 2
yield 3
The generator doesn't create an entire list in memory, it sends each value on demand.
A "lazy" behavior
The yield
keyword enables lazy evaluation: values are only calculated when they are requested.
This allows to:
- Reduce memory consumption;
- Handle continuous or very large streams (thousands of elements);
- Improve streaming performance.
To better understand how it works, let's take an example again. This time with an infinite number generator.
def infinite_numbers():
i = 1
while True:
yield i
i += 1
gen = infinite_numbers()
print(next(gen)) # 1
print(next(gen)) # 2
This function never stops, but consumes almost no memory! 😬
It remembers its last result to increase it by one at each function call: that's the whole point of a generator.
yield and yield from: different but complementary
In Python, the yield
keyword allows producing values one by one in a generator function.
But in some cases, when this function must in turn call another generator, it can quickly become verbose and repetitive.
That's where yield from
comes into play.
Using yield from for simplified delegation
With yield from
, you can directly delegate iteration to a sub-generator or to any iterable (list, tuple, etc).
Here's a small example:
def fruits():
yield from ["🍎", "🍌", "🍓"]
def vegetables():
yield from ["🥦", "🥕"]
def market():
yield "Market start"
yield from fruits()
yield from vegetables()
yield "Market end"
for item in market():
print(item)
We create here three generators:
fruits
- contains the market fruits;vegetables
- contains the vegetables;market
- contains the functioning of our market (well, obviously, it's an example 😉).
Here's its result:
Market start
🍎
🍌
🍓
🥦
🥕
Market end
Each generator is modular, and yield from
allows us to compose them easily without making the logic heavier.
Finally, what you need to remember is that yield
is made to manually produce values.
On its side, yield from
allows easily integrating other generators, which allows us to decompose our code, to make it more maintainable.
Summary table
Let's summarize all this in a small table.
Aspect | yield | yield from |
Produces a single value | ✅ Yes | ❌ No |
Delegates to an iterable | ❌ Requires a loop | ✅ Yes |
Compact syntax | ❌ Less clear for delegation | ✅ Very readable |
Used for | Simple generators | Sub-generators |
Frequently asked questions about yield
Here are the most frequent questions when you're starting with yield
in Python.
What is the difference between
yield
andreturn
?
return
terminates the function. yield
suspends and resumes later.
Can you do a
break
in ayield
?
Yes, like in any loop.
Is it compatible with
async
?
No. For asynchronous, we use async def
and await
.
Can you nest multiple
yield
?
Yes. Even in nested loops and conditions.
Where can I learn Python?
On a complete course, like ours! 😋