Skip to content

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

count = 0

def increment():
    count += 1  # UnboundLocalError!
    return count

increment()  # Error!

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

# In Python 2, this would be integer division
result = 5 / 2  # Python 2: 2, Python 3: 2.5

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

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

def my_function():
    print("Hello")    # Tab
    print("World")  # Spaces - Inconsistent!

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

  1. Print Debugging: Add print statements to trace execution
  2. Use pdb: Python's built-in debugger
  3. IDE Debuggers: Use VS Code, PyCharm debuggers
  4. 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

🔗 Common Programming Mistakes