C.W.K.
Stream
Lesson 07 of 07 · published

Iteration — the Pythonic Patterns Recap

~12 min · recap, idioms, anti-patterns

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

The patterns to internalize

This whole track is about turning "loop and accumulate" into "generate and consume." Once you do, you write code that's easier to compose, cheaper on memory, and shorter. Here's the short list of patterns that compose well together.

Pattern 1 — yield instead of append-then-return

The clearest sign you should write a generator: a function that builds a list with append and returns it, where the caller iterates it once. Convert: drop the initial result = [], change result.append(x) to yield x, drop the return.

Pattern 2 — generator expression in a reducer

When you're going to feed a comprehension into sum, any, all, max, min, or set, drop the brackets. sum(x*x for x in nums) beats sum([x*x for x in nums]) on memory and short-circuiting.

Pattern 3 — pipeline over nested loop

If your big loop does several distinct steps, factor each into a generator. The resulting pipeline reads top-to-bottom and tests each layer independently.

Pattern 4 — itertools before custom code

Before writing your own iterator, check itertools. The combinatoric trio (product, permutations, combinations) replaces almost all hand-rolled nested loops. chain handles concatenation. islice handles slicing. groupby handles consecutive grouping.

Anti-pattern — turning lazy back into eager

Don't call list() in the middle of a pipeline unless you have a specific reason. Don't iterate the same iterator twice and wonder why the second pass is empty. Don't reach for tee when materializing as a list is simpler and clearer.

Pythonic Way: The skill of iteration is knowing when to materialize and when not to. Default to lazy. Materialize when you must — when you need len, indexing, slicing, or repeated iteration. The default of "build a list and return it" is a learned habit from languages without generators; Python lets you do better.

Code

Pattern 1 — yield instead of append·python
# Before — eager
def squares_v1(nums):
    result = []
    for n in nums:
        result.append(n * n)
    return result

# After — lazy
def squares_v2(nums):
    for n in nums:
        yield n * n

# Caller code is identical for `for x in squares(...)` consumption
# But v2 streams without ever building a list
for x in squares_v2(range(5)):
    print(x)
# 0 1 4 9 16
Pattern 2 — generator expression in reducers·python
nums = [1, 2, 3, 4, 5]

# Better — no intermediate list, short-circuits when applicable
print(sum(x*x for x in nums))           # 55
print(any(x > 3 for x in nums))         # True
print(max(x*2 for x in nums))           # 10
print(set(x % 3 for x in nums))         # {0, 1, 2}
Pattern 3 — small composable stages·python
def even_only(nums):
    for n in nums:
        if n % 2 == 0:
            yield n

def double(nums):
    for n in nums:
        yield n * 2

def summed(nums):
    return sum(nums)

# Read top to bottom:
result = summed(double(even_only(range(10))))
print(result)                # 40    (2+4+6+8) doubled = 40
Pattern 4 — itertools first·python
import itertools as it

# Don't write this
result = []
for x in [1, 2, 3]:
    for y in ["a", "b"]:
        result.append((x, y))

# Write this
result = list(it.product([1, 2, 3], ["a", "b"]))
print(result)
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]

External links

Exercise

Take a function process_log that opens a log file, builds a list of (timestamp, level, message) tuples by appending in a loop, returns the list, and the caller filters for errors. Refactor it into three pieces: (a) lines_of(path) — generator yielding stripped lines. (b) parsed(lines) — generator yielding the tuples. (c) errors_only(parsed) — generator yielding only tuples where level == "ERROR". Show that the original caller code (for ts, lvl, msg in errors_only(parsed(lines_of(path))):) needs no other change. Use a small in-memory string instead of a real file for testing.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.