Closures, class attributes and the tuple puzzle
Predict every line first. The last two lines show an error and a side effect from the same statement.
The famous Python puzzles (late-binding closures, class versus instance attributes, tuples holding lists, exhausted generators, integer caching, dict ordering) explained by the rule behind each, so you can predict the output instead of memorising it.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Fix this so the three functions return 0, 1 and 2: fs = [lambda: i for i in range(3)]. Show two different fixes.
Write a function total_and_count(numbers) that works even when numbers is a generator, without looping twice.
Compare 0.1 + 0.2 with 0.3 correctly using the standard library, and print True.
Explain it without notes
Why does [lambda: i for i in range(3)] give three functions that all return 2? How do you fix it?
A class has count = 0. After a.count += 1, what are a.count, b.count and A.count, and why? What changes if count is a list and you call a.count.append(1)?
What happens when you run t = ([1, 2], 3); t[0] += [10]? Does the list change?
Why can you loop over a list twice but over a generator only once?
Is 256 is 256 guaranteed to be True? What about 257 is 257?
A class has tags = []. What do a.tags, b.tags and A.tags show after a.tags += ['y'], and is 'tags' in a.__dict__?
Expected output
late binding: [2, 2, 2]
default arg fix: [0, 1, 2]
a.total: 1 | b.total: 0 | Counter.total: 0
b.tags: ['x'] | 'total' in a.__dict__: True
TypeError: 'tuple' object does not support item assignment
but the list changed anyway: ([1, 2, 10], 3)