10.16 Common Mistakes
The most common list bugs in Python -- off-by-one IndexError, silently skipping elements by mutating a list while iterating over it, and the shared-reference and mutable-default-argument traps.
Index Errors
Accessing an index that doesn’t exist — especially easy off-by-one mistakes near a list’s boundaries.
>>> l = [1, 2, 3]
>>> l[5]
Traceback (most recent call last):
IndexError: list index out of range
Modifying a List While Iterating Over It
Removing items from a list while iterating over it directly causes elements to be silently skipped, because the indices shift underneath the iterator as items are removed.
# WRONG: mutating the list you're iterating over
l = [1, 2, 3, 4, 5]
for x in l:
if x % 2 == 0:
l.remove(x) # shifts remaining elements -- some get skipped!
# CORRECT: iterate over a COPY, mutate the original
l = [1, 2, 3, 4, 5]
for x in l[:]:
if x % 2 == 0:
l.remove(x)
>>> l
[1, 3, 5]
Shared References
Forgetting that b = a shares the same list object rather than copying it — see 10.11 Copying and Mutability for the full explanation and the shallow-vs-deep distinction.
Mutable Default Arguments
Using a list as a function’s default argument value — the default is created once at definition time and shared across every call that doesn’t override it. Fully explained, with the fix, in 10.11 Copying and Mutability and 5.15 Common Mistakes.
Quick Interview Answer
“Four mistakes account for most list bugs: off-by-one
IndexErrors near a list’s boundary; mutating a list while iterating over it directly, which silently skips elements because removal shifts every later index backward — the fix is iterating over a copy (for x in l[:]) while mutating the original; forgettingb = ashares one list rather than copying it, so a mutation throughbis visible throughatoo; and the mutable-default-argument trap, where a list default is created once at function-definition time and silently accumulates across calls unlessNoneis used as the sentinel instead.”
Common Mistakes
- Removing items from a list with
l.remove(x)ordel l[i]inside afor x in l:loop over that same list — build a new filtered list, use a comprehension, or iterate overl[:]instead. - Assuming two separately-created lists that happen to look identical (
[1, 2] == [1, 2]) are the same object —==compares content,iscompares identity; see 10.6 List Operators. - Defining
def f(items=[])and being surprised the default list accumulates values across unrelated calls — default toNoneand create the list inside the function body.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form