15 Common Python Mistakes Beginners Make
From mutable default arguments to 'is' vs '==', modifying a list while iterating, and integer division surprises — here are the most common Python mistakes and exactly how to avoid each one.
Most Python bugs for beginners come from a familiar short list — mutable default arguments, is vs ==, modifying a list while iterating, and integer-division surprises chief among them. Knowing them in advance saves hours of debugging and is exactly what interviewers probe. Here are 15, each with the fix.
1. Mutable default arguments
The classic trap — one shared list across all calls:
def add(item, items=[]): # BUG: items is shared!
items.append(item)
return items
def add(item, items=None): # FIX
items = items or []
items.append(item)
return items
2. is vs ==
== compares values; is compares identity (same object). Use == for values, is only for None:
if x is None: # correct
if x == None: # works but non-idiomatic
if name is "Ana": # BUG — compares identity, not value
3. Modifying a list while iterating it
Removing items mid-loop skips elements. Iterate a copy or rebuild:
for x in items[:]: # iterate a copy
if unwanted(x): items.remove(x)
items = [x for x in items if not unwanted(x)] # cleaner
4. / vs //
/ always gives a float (7 / 2 == 3.5); // floors (7 // 2 == 3). Mixing them up produces wrong types.
5. Indentation errors
Python uses indentation for blocks — mixing tabs and spaces, or inconsistent levels, raises IndentationError. Pick four spaces and stay consistent.
6. Mutable vs immutable confusion
Lists and dicts are mutable; strings and tuples aren’t. Expecting "abc"[0] = "x" to work raises an error. See lists vs tuples vs sets vs dicts.
7. Copying references, not data
b = a copies the reference — mutate b and a changes too. Use a.copy() or list(a); for nested structures, copy.deepcopy.
8. Mutable class attributes
A list defined at class level is shared by all instances. Initialise per-object state in __init__ (self.items = []). See Python OOP.
9. Catching exceptions too broadly
except: swallows everything, including KeyboardInterrupt and real bugs. Catch specific exceptions: except ValueError:.
10. Late-binding closures in loops
funcs = [lambda: i for i in range(3)]
[f() for f in funcs] # [2, 2, 2], not [0, 1, 2]!
funcs = [lambda i=i: i for i in range(3)] # FIX: capture i now
11. Floating-point comparison
0.1 + 0.2 == 0.3 is False. Compare with math.isclose(a, b).
12. Forgetting self
Every instance method needs self as its first parameter, and attributes are accessed via self.x. Omitting it is the #1 OOP beginner error.
13. Using range(len(x)) to iterate
Pythonic code iterates directly: for item in items, or for i, item in enumerate(items) when you need the index — not for i in range(len(items)).
14. Confusing append and extend
list.append([1, 2]) adds the list as one element; list.extend([1, 2]) adds each element. Mixing them up produces nested lists you didn’t want.
15. Shadowing built-in names
Naming a variable list, dict, str, or id overwrites the built-in for that scope, causing baffling errors later. Avoid built-in names.
None), and never mutate a collection while iterating it (iterate a copy or build a new one). Together they kill the most insidious Python bugs.
Where this fits
Avoiding these runs through the Python roadmap, and most trace back to mutability or OOP basics.
Every one of these is pre-empted with a diagram and a fix in Python in One Month and the job-ready Python in Three Months — one gotcha per page is the whole approach.
Learn the traps before they bite, and Python debugging gets far quieter.
Frequently asked questions
What is the most common Python mistake?
The mutable default argument trap: writing def f(items=[]) creates one shared list reused across all calls, so changes persist between calls unexpectedly. The fix is to default to None and create the list inside the function: def f(items=None): items = items or [].
What is the difference between 'is' and '==' in Python?
== checks whether two values are equal; 'is' checks whether they are the exact same object in memory. Use == for value comparison (the usual case) and 'is' only for identity checks, most commonly 'is None'. Using 'is' to compare values can give wrong results.
Why can't I modify a list while looping over it in Python?
Removing or adding items while iterating shifts the indices the loop relies on, so elements get skipped or processed twice. Instead, iterate over a copy (for x in list[:]), or build a new list with a comprehension containing only the items you want to keep.
What is the difference between / and // in Python?
/ is true division and always returns a float (7 / 2 is 3.5), while // is floor division and returns the result rounded down (7 // 2 is 3). Forgetting this is a common source of bugs when you expect an integer but get a float, or vice versa.