DEV Community

Ameer Abdullah
Ameer Abdullah

Posted on

Five Python Patterns Every Developer Should Be Able to Trace on Sight

After analyzing hundreds of Python interview questions and production bug reports, five patterns account for the majority of prediction errors and bugs that experienced developers encounter.

If you can trace each of these on sight, without hesitation, you are prepared for virtually any Python dry-run question.


Pattern 1: The Shared Mutable Default

def build_list(item, container=[]):
    container.append(item)
    return container

r1 = build_list("a")
r2 = build_list("b")
r3 = build_list("c", [])
print(r1)
print(r2)
print(r3)
Enter fullscreen mode Exit fullscreen mode

Output:

['a', 'b']
['a', 'b']
['c']
Enter fullscreen mode Exit fullscreen mode

r1 and r2 share the default list because it is created once at function definition time. r3 receives a new empty list explicitly so it is independent.

Trace rule: any call that does not pass the second argument shares the single default object.


Pattern 2: The Generator That Forgets

def make_gen(n):
    for i in range(n):
        yield i * 2

g = make_gen(4)
first_half = list(g)[:2]
second_half = list(g)
print(first_half)
print(second_half)
Enter fullscreen mode Exit fullscreen mode

Output:

[0, 2]
[]
Enter fullscreen mode Exit fullscreen mode

list(g) exhausts the generator. The first list(g) produces [0, 2, 4, 6] and then takes the first two elements. The generator is now exhausted. The second list(g) finds no remaining values.

Trace rule: calling list() on a generator always consumes it entirely.


Pattern 3: The Class Variable Mutation

class Registry:
    items = []

    @classmethod
    def register(cls, item):
        cls.items.append(item)

Registry.register("first")
r = Registry()
r.register("second")
Registry.register("third")
print(Registry.items)
print(r.items)
Enter fullscreen mode Exit fullscreen mode

Output:

['first', 'second', 'third']
['first', 'second', 'third']
Enter fullscreen mode Exit fullscreen mode

All three registrations go to the same class-level list. The instance r has no instance attribute items so it reads the class attribute. Both Registry.items and r.items reference the same list.

Trace rule: class variables are shared unless overridden by instance assignment.


Pattern 4: The Late Binding in a Loop

multipliers = {}
for i in range(1, 4):
    multipliers[i] = lambda x: x * i
print(multipliers[1](10))
print(multipliers[2](10))
print(multipliers[3](10))
Enter fullscreen mode Exit fullscreen mode

Output:

30
30
30
Enter fullscreen mode Exit fullscreen mode

All three lambdas close over the same variable i. By the time they are called, the loop has finished and i is 3. Every lambda returns x * 3 regardless of which key you use.

Fix:

multipliers[i] = lambda x, i=i: x * i
Enter fullscreen mode Exit fullscreen mode

The default parameter captures the current value of i at lambda creation time.


Pattern 5: The Chained Comparison With Side Effects

def check(n):
    print(f"checking {n}")
    return n

result = 1 < check(2) < 3
print(result)
Enter fullscreen mode Exit fullscreen mode

Output:

checking 2
True
Enter fullscreen mode Exit fullscreen mode

Python evaluates check(2) once and reuses the result in both comparisons. It does not call check twice. The expression becomes 1 < 2 and 2 < 3 which is True and True which is True.

This matters in production code when the middle expression has side effects: it runs exactly once.


The Unified Mental Model

All five patterns follow from the same underlying principle: Python names are references to objects, and the behavior you observe depends on whether operations create new objects or modify existing ones.

Default arguments, class variables, and closure variables are all cases where a single object is referenced from multiple places. Late binding is a case where the reference is resolved at call time, not at definition time. Chained comparison is a case where Python optimizes to avoid redundant evaluation.

Once you internalize references versus objects as your mental model, each pattern becomes predictable rather than surprising.

For daily practice on all five patterns and hundreds of variations, visit pycodeit.com. Free, no account needed to start.

Top comments (0)