Both coroutines and generators use Python’s yield
keyword, but they serve different purposes. Here’s a breakdown:
- A generator is a function that produces a sequence of values lazily (on-demand) using
yield
. - It pauses execution at each
yield
and resumes when the next value is requested (e.g., in afor
loop).
- Single-directional: Data flows out of the generator (via
yield
). - Used for iteration: Ideal for large/streaming datasets (e.g., reading files line-by-line).
- Stateful: Remembers its state between
yield
s.
def count_up_to(n):
i = 1
while i <= n:
yield i # Pauses here and returns `i`
i += 1
# Usage
for num in count_up_to(5):
print(num) # Output: 1, 2, 3, 4, 5
- A coroutine is a generalization of generators that can consume data (via
yield
) and maintain state between executions. - They allow two-way communication: You can send data into a coroutine using
.send()
.
- Bi-directional: Data flows in (via
.send()
) and out (viayield
). - Used for cooperative multitasking: Lightweight concurrency (e.g., async I/O).
- More complex: Can
yield
values while also receiving inputs.
def echo():
while True:
received = yield # Pauses and waits for a value via `.send()`
print(f"Received: {received}")
# Usage
coro = echo()
next(coro) # Prime the coroutine (runs until first `yield`)
coro.send("Hi") # Output: "Received: Hi"
coro.send("Bye") # Output: "Received: Bye"
Feature | Generators | Coroutines |
---|---|---|
Purpose | Produce values lazily. | Consume/produce values + pause/resume. |
Data Flow | One-way (yield out). |
Two-way (yield + .send() in). |
Initialization | Called directly (next(gen) ). |
Must be "primed" with next(coro) . |
Use Case | Iteration, memory efficiency. | Concurrency, pipelines, stateful tasks. |
Python Version | Since Python 2.2. | Enhanced in Python 2.5+. |
- Both use
yield
to pause/resume execution. - Coroutines are built on generators (a coroutine is a superset of a generator).
- Modern Python uses
async
/await
for coroutines (more readable than rawyield
).
- Streaming large files.
- Generating infinite sequences (e.g., Fibonacci numbers).
- Memory-efficient pipelines.
- Cooperative multitasking (e.g., event loops).
- Stateful processing (e.g., parsing tokens).
- Lightweight concurrency (pre-
asyncio
).
- Coroutines are now typically written with
async def
andawait
. - Replaces explicit
.send()
/yield
with cleaner syntax:async def fetch_data(): data = await some_io_operation() # Pauses until I/O completes return data
- Generators: Pull values out with
yield
(for iteration). - Coroutines: Push values in with
.send()
+ pull values out (for concurrency). - Modern Python: Prefer
async
/await
for coroutines.