Python Common Mistakes¶
This guide covers common Python programming mistakes and how to avoid them, with practical solutions and examples.
🚨 Most Common Python Errors¶
1. Mutable Default Arguments¶
Using mutable objects as default arguments leads to unexpected behavior.
Problem¶
def add_item(item, items=[]): # Same list used across calls!
items.append(item)
return items
# First call
result1 = add_item("apple") # ['apple']
print(result1)
# Second call - unexpected behavior!
result2 = add_item("banana") # ['apple', 'banana']
print(result2) # ['apple', 'banana'] - previous item still there!
Solutions¶
# Solution 1: Use None as default
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
# Solution 2: Use type hints and default factory
from typing import List, Optional
def add_item(item: str, items: Optional[List[str]] = None) -> List[str]:
if items is None:
items = []
items.append(item)
return items
2. Comparison with None¶
Using == instead of is for None comparison.
Problem¶
def process_value(value):
if value == None: # Wrong comparison!
return "No value"
return str(value)
# This can fail with custom __eq__ methods
class CustomNone:
def __eq__(self, other):
return True
custom_none = CustomNone()
print(process_value(custom_none)) # "No value" - unexpected!
Solutions¶
# Solution 1: Use 'is' for identity comparison
def process_value(value):
if value is None: # Correct identity comparison
return "No value"
return str(value)
# Solution 2: Use truthiness for general cases
def process_value(value):
if not value: # Handles None, empty strings, 0, etc.
return "No value"
return str(value)
3. List/Dictionary Modification During Iteration¶
Modifying a collection while iterating over it leads to unexpected behavior.
Problem¶
# Bad: Modifying list while iterating
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers:
if num % 2 == 0:
numbers.remove(num) # Modifying list while iterating!
print(numbers) # [1, 3, 5, 6] - 6 was skipped!
Solutions¶
# Solution 1: Create new list
numbers = [1, 2, 3, 4, 5, 6]
odd_numbers = [num for num in numbers if num % 2 != 0]
print(odd_numbers) # [1, 3, 5]
# Solution 2: Iterate over copy
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers.copy(): # Iterate over copy
if num % 2 == 0:
numbers.remove(num)
print(numbers) # [1, 3, 5]
# Solution 3: Use list comprehension
numbers = [1, 2, 3, 4, 5, 6]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers) # [1, 3, 5]
4. Variable Scope Issues¶
Understanding local vs. global variable scope.
Problem¶
Solutions¶
# Solution 1: Use global keyword
count = 0
def increment():
global count
count += 1
return count
# Solution 2: Pass as parameter (better practice)
def increment(current_count):
return current_count + 1
count = 0
count = increment(count)
5. String Concatenation in Loops¶
Inefficient string concatenation using + operator.
Problem¶
# Bad: Inefficient string concatenation
words = ["hello", "world", "python"] * 1000
result = ""
for word in words:
result += word + " " # Creates new string each time!
Solutions¶
# Solution 1: Use join method
words = ["hello", "world", "python"] * 1000
result = " ".join(words) # Efficient
# Solution 2: Use StringIO for complex operations
from io import StringIO
buffer = StringIO()
for word in words:
buffer.write(word + " ")
result = buffer.getvalue()
🔧 Logic Errors¶
6. Off-by-One Errors¶
Incorrect indexing or loop boundaries.
Problem¶
# Process first 5 elements
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, 6): # Should be range(5)
print(numbers[i]) # Processes 6 elements!
Solutions¶
# Solution 1: Correct range
for i in range(5): # 0, 1, 2, 3, 4
print(numbers[i])
# Solution 2: Use slicing
first_five = numbers[:5] # More Pythonic
for num in first_five:
print(num)
# Solution 3: Use enumerate for safety
for i, num in enumerate(numbers[:5]):
print(f"Index {i}: {num}")
7. Integer Division¶
Unexpected results from integer division in Python 2 vs Python 3.
Problem¶
Solutions¶
# Solution 1: Use float division
result = 5 / 2 # 2.5 in both Python 2 and 3
# Solution 2: Use // for integer division when needed
result = 5 // 2 # 2 (floor division)
# Solution 3: Explicit float conversion
result = float(5) / 2 # 2.5
🎯 Type-Related Mistakes¶
8. Shallow vs. Deep Copy¶
Modifying copies when you think you're working with originals.
Problem¶
original = [1, [2, 3], 4]
copy = original.copy() # Shallow copy!
copy[1][0] = 99
print(original) # [1, [99, 3], 4] - Original modified!
Solutions¶
# Solution 1: Deep copy
import copy
original = [1, [2, 3], 4]
deep_copy = copy.deepcopy(original)
deep_copy[1][0] = 99
print(original) # [1, [2, 3], 4] - Original unchanged
# Solution 2: List comprehension for simple cases
original = [1, [2, 3], 4]
deep_copy = [item.copy() if hasattr(item, 'copy') else item for item in original]
9. Mixing Tabs and Spaces¶
Inconsistent indentation causing IndentationError.
Problem¶
Solutions¶
# Solution 1: Configure editor to show whitespace
# Solution 2: Use spaces consistently (PEP 8 recommends 4 spaces)
def my_function():
print("Hello") # 4 spaces
print("World") # 4 spaces
# Solution 3: Configure editor to convert tabs to spaces
🛠️ Performance Mistakes¶
10. Inefficient Membership Testing¶
Using lists for frequent membership tests.
Problem¶
# Bad: O(n) lookup for each test
valid_colors = ['red', 'green', 'blue', 'yellow', 'purple']
for color in user_colors: # user_colors has 1M items
if color in valid_colors: # O(n) for each check!
process_color(color)
Solutions¶
# Solution 1: Use set for O(1) lookup
valid_colors = {'red', 'green', 'blue', 'yellow', 'purple'}
for color in user_colors:
if color in valid_colors: # O(1) lookup!
process_color(color)
# Solution 2: Use frozenset for immutable sets
valid_colors = frozenset(['red', 'green', 'blue', 'yellow', 'purple'])
11. Reinventing the Wheel¶
Not using built-in functions and libraries.
Problem¶
# Bad: Manual implementation of built-in functionality
def find_max(numbers):
max_val = numbers[0]
for num in numbers:
if num > max_val:
max_val = num
return max_val
# Bad: Manual sorting
def sort_list(items):
sorted_items = []
while items:
smallest = min(items)
sorted_items.append(smallest)
items.remove(smallest)
return sorted_items
Solutions¶
# Good: Use built-in functions
max_val = max(numbers) # Built-in, optimized
sorted_items = sorted(items) # Built-in, optimized
# Good: Use standard library
import statistics
mean_val = statistics.mean(numbers) # Statistical functions
median_val = statistics.median(numbers)
🐛 Debugging Tips¶
Common Debugging Strategies¶
- Print Debugging: Add print statements to trace execution
- Use pdb: Python's built-in debugger
- IDE Debuggers: Use VS Code, PyCharm debuggers
- Logging: Use logging module for production debugging
Debugging Checklist¶
- Check for None values before method calls
- Verify list indices are within bounds
- Ensure consistent indentation
- Use appropriate data structures for operations
- Check for mutable default arguments
- Verify string comparison methods
Using pdb Effectively¶
import pdb
def buggy_function(data):
result = []
for item in data:
pdb.set_trace() # Breakpoint here
processed = process_item(item)
result.append(processed)
return result
# pdb commands:
# n - next line
# s - step into function
# c - continue execution
# p variable - print variable value
# q - quit debugger
📚 Related Resources¶
- Python Best Practices - Prevent these mistakes with good practices
- Python Performance Tips - Optimize your Python code
- Python Testing Frameworks - Catch mistakes early with testing
- Python Resources - Learning materials and tools
🔗 Common Programming Mistakes¶
- Logic Errors - General logic mistakes
- Runtime Errors - Common runtime issues
- Syntax Errors - Syntax and indentation errors