Skip to content

Generators & yield

Generators exist in both languages — but Python leans on them harder

Section titled “Generators exist in both languages — but Python leans on them harder”

TypeScript has generators too: function* with yield works almost identically at first glance. You get a lazy iterator, you can call .next(), and the function suspends between yields. But in Python, generators are not a niche feature — they are woven into the standard library. range(), file iteration, zip(), map(), filter(), and the entire itertools module all return lazy iterators built on the same generator protocol. Even async/await in Python is built on top of generators internally. If you understand Python generators, you understand a large slice of the language.

A Python generator is any function that contains a yield statement. Calling it returns a generator object — no code runs until you iterate.

TypeScript
// TypeScript generator with function*
function* countUp(start: number, stop: number): Generator<number> {
let current = start;
while (current <= stop) {
yield current;
current++;
}
}
const gen = countUp(1, 5);
for (const val of gen) {
process.stdout.write(val + " ");
}
console.log();
// Infinite generator
function* fibonacci(): Generator<number> {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
const firstTen = Array.from({ length: 10 }, () => fib.next().value);
console.log(firstTen);
Python
# Python generator with def + yield
def count_up(start, stop):
current = start
while current <= stop:
yield current
current += 1
gen = count_up(1, 5)
for val in gen:
print(val, end=" ")
print()
# Infinite generator
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
first_ten = [next(fib) for _ in range(10)]
print(first_ten)

Key differences to notice:

  • Python uses a plain def — there is no function* syntax. The presence of yield anywhere in the function body makes it a generator automatically.
  • Python’s for loop works directly with any iterable, including generators, without needing to call .next() manually.
  • TypeScript requires annotating the return type as Generator<T>; Python infers it automatically.

Python generators support two-way data flow. You can send a value back into a paused generator using .send(). The sent value becomes the result of the yield expression inside the generator. TypeScript generators support this via gen.next(value), but the feature is less commonly used in the TS ecosystem.

TypeScript
// TypeScript: sending values into a generator
function* accumulator(): Generator<number, void, number> {
let total = 0;
while (true) {
const value = yield total;
if (value === undefined) break;
total += value;
}
}
const acc = accumulator();
acc.next(); // prime the generator
console.log(acc.next(10).value); // 10
console.log(acc.next(20).value); // 30
console.log(acc.next(5).value); // 35
Python
# Python: sending values into a generator with .send()
def accumulator():
total = 0
while True:
value = yield total
if value is None:
break
total += value
acc = accumulator()
next(acc) # prime the generator (advance to first yield)
print(acc.send(10)) # 10
print(acc.send(20)) # 30
print(acc.send(5)) # 35
def count_up(start, stop):
current = start
while current <= stop:
yield current
current += 1
gen = count_up(1, 5)
for val in gen:
print(val, end=" ")
print()
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
first_ten = [next(fib) for _ in range(10)]
print(first_ten)
What makes a Python function a generator?
What exception does a generator raise when it is fully exhausted?
What does gen.send(value) do?
What is the purpose of yield from?