Somebody in every comment section eventually drops one of these with "bet you can't predict the output." I got tired of guessing wrong in public, so I ran all twelve myself, in Python 3.12, and wrote down what happened.
The game: run each snippet, or at least commit to a guess, before you read the line under it. Wrong here costs nothing. Wrong about number 2 in production costs a weekend.
1. The string that lies to you
print(bool("False")) It prints True.
Python never reads the word. It asks one question about a string: is it empty? "False" has five characters in it, so it passes. "0" is truthy too. The only string that fails is "".
2. The default that remembers 🪤
def add(item, box=[]): box.append(item) return box print(add(1)) print(add(2)) You get [1], then [1, 2].
The default list is built once, when Python reads the def, and every call that doesn't pass its own box gets that same list. This is the most re-posted bug in Python history, and it still shows up in real codebases as an accumulator or a cache that quietly holds last Tuesday's data. Fix is boring. Default to None, build the list inside.
3. The sum everyone has seen
print(0.1 + 0.2 == 0.3) print(0.1 + 0.2) False, then 0.30000000000000004.
Neither 0.1 nor 0.2 exists exactly in binary, so the machine adds two very good approximations and hands you the honest total of the wrong numbers. I wrote a whole separate post about why.
4. The comparison that chains 🪤
print(False == False in [False]) True. And it took me two reads to see why.
Python chains comparisons. That line means False == False and False in [False], both of which are true. Chaining is lovely for 1 < x < 10. Mix == and in into the chain and it stops reading like English while still running like logic.
5. The loop of identical lambdas 🪤
funcs = [lambda: i for i in range(3)] print([f() for f in funcs]) [2, 2, 2].
Each lambda doesn't grab the value of i. It grabs the variable i, and by the time anything calls those functions the loop is finished and i is sitting at 2. It eats people alive in GUI code: build a column of buttons in a loop and every button ends up doing the last thing. Fix: lambda i=i: i, which pins the current value as a default. Ugly. Works.
6. Equal, but not the same
print([] == []) print([] is []) True, then False.
== asks if two things have the same value. is asks if they're the same object. Two empty lists have equal value and separate existences. Confuse them and you'll write a check that passes in testing and fails on a Tuesday.
7. The empty list that passes everything
print(any([])) print(all([])) False, then True.
all() on an empty list is true because there's nothing in there to disagree. Logicians call it vacuous truth; production code calls it "wait, why did validation pass with no checks loaded?" If all(checks) can ever meet an empty checks, that's an incident report.
8. The comma is the tuple
print(type((1,))) print(type((1))) <class 'tuple'>, then <class 'int'>.
The parentheses do nothing. The comma is what builds the tuple, which is why (1) is just a 1 wearing a jacket. Everyone trips on this once, usually returning a single value from a function and watching the other end unpack it wrong.
9. The sort that refuses
sorted([3, 'a']) It doesn't print anything. It dies: TypeError: '<' not supported between instances of 'str' and 'int'.
Python 3 will not rank a number against a letter. Python 2 would, silently, in an order that meant nothing. The crash is the language doing you a favour, loudly, before nonsense gets saved to a database.
10. Zero is False, but nothing else is
print(0 == False) print('' == False) True, then False.
Booleans are integers underneath in Python, so False is 0 and 0 == False holds. A string never equals a boolean, though. Write if x == False and these two lines are the crack it falls through.
11. The rounding that goes down 🪤
print(round(2.5)) print(round(3.5)) 2, then 4.
Python rounds ties to the nearest even number. Bankers rounding. It exists to stop rounding error drifting one way at scale. Fair enough. But code that assumes .5 always goes up, a grade boundary, a price, will produce a number somebody's spreadsheet disagrees with.
12. The value that isn't equal to itself
x = float('inf') - float('inf') print(x) print(x == x) nan, then False.
Infinity minus infinity isn't zero. It's undefined, and Python calls that nan, a value the standard defines as unequal to everything, including itself. That's why x != x works as a nan detector.
So why are four of these traps?
The other eight announce themselves. A TypeError stops the show. A weird boolean surprises you once and you learn. The traps fail silently inside code that looks correct: a default list shared between calls, a loop full of callbacks holding one variable, a chained comparison, a rounding rule nibbling at money. Nothing crashes. The numbers just come out plausibly wrong, and plausible wrong numbers are the hardest kind to catch.
The honest catch: none of this is a bug in Python. Every behaviour above is documented. That's precisely the problem. Nobody reads the documentation for round() until round() has cost them an afternoon.
My scoresheet, for the record: I guessed 8 of 12 cold. The lambdas got me. They always get me.
How many did you get right before peeking? And which of the twelve has actually bitten you in real code, not a quiz. My money is on the default argument.
P.S. Everything I write stays free — that part doesn't change. But if this one was worth something to you, you can buy me a coffee ☕ (one-time or monthly, both work). It covers the API credits and hosting behind these experiments — and honestly, it's also the nudge that gets the next one started on the evenings I'd otherwise skip it. — Salam
