Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Functional Python  ›  Lesson

Generator Expressions

Generator Expressions 16 minA comprehension's lazy sibling — one value at a time, never the whole collection in memory
Your task
Write first_over_budget(orders, budget). Build a generator expression that keeps only the amounts above budget, then use next(..., None) to return the first one, without ever building a list of all matches. Example: first_over_budget([50, 80, 120, 200], 100) -> 120 · first_over_budget([10, 20], 100) -> None.

Generator Expressions

In short: (expr for item in iterable if cond) looks exactly like a list comprehension, but with ( ) instead of [ ]. The difference is what it hands back: a list comprehension builds the whole collection in memory, right away; a generator expression hands out one value at a time, only when asked, and never stores the rest. Feed it straight to sum(), any(), all(), max() or next() and the list is never built at all. Drop the parentheses when it's the only argument to a call: sum(x for x in nums) reads cleaner than sum((x for x in nums)). Like map and filter, a generator is used up after one full pass.

A lazy tap dripping one numbered drop at a time into a hand, labelled "(x for x in nums)" — next to it, a full bucket already holding every drop at once, labelled "[x for x in nums]", with a note under the tap: "only what's asked for, ever exists at once"

Total a huge order log

A store's order log has a million rows. Summing the prices doesn't need all of them sitting in memory at once — it only ever needs the running total and whichever price it's looking at right now:

python

Same answer, wildly different memory. sys.getsizeof on the generator reports a small, fixed size — it's a tiny machine that knows how to produce the next value, not a container holding a million of them.

Stop at the first order over budget

next(gen, default) pulls exactly one value and stops — perfect for "find the first thing that matches" without scanning, building, or even looking at, everything after it:

python

The generator remembers exactly where it paused. The second next() doesn't rescan from the start — it picks up right after 120 and finds 200. A list comprehension would have built [120, 200] in full before you ever asked for the first item.

The reuse trap

A generator is a one-way street. Once it's been walked, it reports nothing else — even a fresh for loop over it finds it empty:

broken — fix it

Run it and notice the total is wrong, not an error. Then get both the highest value and the true total from the same data.

Fix it by keeping a list when you need more than one pass: readings = [12, 45, 8, 90], then max(readings) and sum(readings) both work, because a list can be walked again and again. Only reach for a generator expression when the data is walked exactly once.

Quick practice

What does this print?
nums = [1, 2, 3]
squares = (n * n for n in nums)
print(type(squares).__name__)
What does this print?
nums = [-3, 5, -1, 8]
print(any(n < 0 for n in nums))
What does this print?
g = (x for x in range(3))
first_pass = list(g)
second_pass = list(g)
print(first_pass, second_pass)
What does this print?
prices = [100, 250, 80]
total = sum(p for p in prices if p > 90)
print(total)

🎯 Your turn

Write first_over_budget(orders, budget). Build a generator expression that keeps only the order amounts above budget, then use next(..., None) to return the first one — without ever building a list of all of them. Return None if nothing qualifies.

  • first_over_budget([50, 80, 120, 200], 100) → 120
  • first_over_budget([10, 20], 100) → None

Hint: over = (o for o in orders if o > budget), then return next(over, None).

Press ▶ Run, try the Live App chips, then ✓ Check. After that, the practice set below goes from a single filtered sum to a small stats report built entirely out of generators.

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upSum only what's above a threshold

Write total_over(nums, threshold) returning the sum of every number in nums greater than threshold, using a generator expression fed straight into sum() — no list, no parentheses of its own since it's the only argument.

generator-expression
DrillIs anything negative?

Write any_negative(nums) returning True if any number in nums is negative, else False, using any() with a generator expression (so it can stop at the first match).

generator-expression
BuildCount without building a list

Write count_matches(words, prefix) returning how many strings in words start with prefix, using sum(1 for ... if ...) — a common idiom for counting with a generator.

generator-expression
BossStop at the first big square

Write first_square_over(nums, limit) returning the first value of n * n (for n in nums, in order) that is greater than limit, using a generator expression and next(..., None). Return None if none qualify.

generator-expressionnext
CapstoneA stats report built entirely from generators

Write batch_stats(nums) returning {"total": ..., "count_positive": ..., "first_negative": ...}: the sum of all numbers, how many are positive, and the first negative number (or None if there isn't one). Use a generator expression for each — no list should ever be built.

generator-expressionnext
first_over_budget.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
first_over_budget(orders, budget) → intThe first order amount above budget, found lazily, or None if none qualify.
try:
returned
No app map for this section yet.
Press Run to execute your code.
Press Check to run the tests for this lesson.