Every Python developer makes the same mistakes at the start. The good news — knowing them in advance will save you hours of debugging.
1. Modifying a List While Iterating
# ❌ Wrong — list changes mid-loop:
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 0:
numbers.remove(num) # Skips elements!
print(numbers) # [1, 3, 5] — looks right, but it's luck
Why it’s bad: Python skips elements when you remove them, because indices shift.
# ✅ Correct — create a new list:
numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers) # [1, 3, 5]
2. Mutable Default Argument
# ❌ Classic trap:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("яблоко")) # ['яблоко']
print(add_item("банан")) # ['яблоко', 'банан'] — WTF?!
print(add_item("апельсин")) # ['яблоко', 'банан', 'апельсин']
Why it’s bad: The list is created once when the function is defined, not on every call.
# ✅ Use None as the default:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item("яблоко")) # ['яблоко']
print(add_item("банан")) # ['банан']
3. Comparing with None Using ==
# ❌ Works, but stylistically wrong:
result = None
if result == None:
print("No result")
# ✅ Always use is / is not:
if result is None:
print("No result")
if result is not None:
print(f"Result: {result}")
Why: == checks value equality, is checks object identity. None is a singleton — is is more correct and faster.
4. Catching All Exceptions
# ❌ Bad — hides real errors:
try:
result = int(input("Enter a number: "))
print(10 / result)
except:
print("Something went wrong")
If you have a typo in a variable name — you’ll never know.
# ✅ Catch specific exceptions:
try:
result = int(input("Enter a number: "))
print(10 / result)
except ValueError:
print("That's not a number!")
except ZeroDivisionError:
print("Can't divide by zero!")
5. Copying Lists via Assignment
# ❌ This is not a copy — it's a reference to the same object:
original = [1, 2, 3]
copy = original
copy.append(4)
print(original) # [1, 2, 3, 4] — the original changed too!
# ✅ Three ways to make a real copy:
copy1 = original.copy()
copy2 = original[:]
copy3 = list(original)
copy1.append(4)
print(original) # [1, 2, 3] — original untouched
For nested structures, use copy.deepcopy():
import copy
matrix = [[1, 2], [3, 4]]
deep = copy.deepcopy(matrix)
deep[0].append(99)
print(matrix) # [[1, 2], [3, 4]] — unchanged
6. Using + for String Concatenation in a Loop
# ❌ Slow — creates a new string on every iteration:
words = ["Python", "это", "супер"]
result = ""
for word in words:
result = result + word + " "
# ✅ Use join():
words = ["Python", "это", "супер"]
result = " ".join(words)
print(result) # Python это супер
Speed difference: With 10,000 strings, join() is orders of magnitude faster.
7. Confusing is and ==
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same values
print(a is b) # False — different objects in memory
c = a
print(a is c) # True — the same object
Rule: == for comparing values. is only for None, True, False, and identity checks.
8. Forgotten return in a Function
# ❌ Function returns None instead of the result:
def multiply(a, b):
result = a * b
# Forgot return!
value = multiply(3, 4)
print(value) # None
# ✅ Always explicitly return the result:
def multiply(a, b):
return a * b
value = multiply(3, 4)
print(value) # 12
9. Misusing Global Variables
# ❌ Unexpected behavior:
counter = 0
def increment():
counter += 1 # UnboundLocalError!
increment()
Python treats counter as a local variable (because of +=), but it’s not defined locally.
# ✅ Option 1 — declare global (not recommended):
def increment():
global counter
counter += 1
# ✅ Option 2 — pass and return the value (better):
def increment(counter):
return counter + 1
counter = increment(counter)
10. Comparing Floats for Equality
# ❌ Never do this:
result = 0.1 + 0.2
print(result == 0.3) # False — floating-point precision error
# ✅ Use math.isclose():
import math
print(math.isclose(result, 0.3)) # True
# Or set an acceptable tolerance:
print(abs(result - 0.3) < 1e-9) # True
11. Forgetting Parentheses When Calling a Method
# ❌ Common mistake — getting a reference to the method, not calling it:
my_list = [3, 1, 2]
my_list.sort # Nothing happened!
# ✅ Correct:
my_list.sort() # Sorts in place
print(my_list) # [1, 2, 3]
12. Using print for Debugging Instead of assert
# Works, but clutters the code:
def divide(a, b):
print(f"a={a}, b={b}") # Debug print
return a / b
# ✅ Use assert to check conditions:
def divide(a, b):
assert b != 0, "Divisor cannot be zero"
return a / b
# ✅ For serious debugging — the logging module:
import logging
logging.debug(f"Calling divide({a}, {b})")
Self-Review Checklist 📋
Before saying “code is ready”, check:
✅ Not modifying a list during iteration?
✅ No mutable default arguments?
✅ Comparing with None using is / is not?
✅ Catching specific exceptions, not all?
✅ Copying lists with .copy(), not =?
✅ Concatenating strings with join(), not +?
✅ Every function has an explicit return?
✅ Not comparing floats with ==?
✅ Not forgetting parentheses when calling methods?
Summary 🚀
These mistakes don’t make you a bad programmer — they make you a normal beginner. The difference between a junior and an experienced developer isn’t that the experienced one never makes mistakes — they just know where to look and find them quickly.
Print the checklist and keep it handy until these things become reflexes. 💪
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!