6.1 KiB
6.1 KiB
Python Anti-Patterns and Fixes
Common Python mistakes and their corrections.
1. Mutable Default Arguments
Anti-pattern:
def add_item(item, items=[]): # WRONG
items.append(item)
return items
Why it's wrong: The list is created once when the function is defined, not each time it's called.
Fix:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
2. Bare Except Clauses
Anti-pattern:
try:
risky_operation()
except: # WRONG - catches everything, including KeyboardInterrupt
handle_error()
Fix:
try:
risky_operation()
except Exception as e: # Or specific exception types
logger.error(f"Operation failed: {e}")
handle_error()
3. Using == for None Comparisons
Anti-pattern:
if value == None: # WRONG
...
Fix:
if value is None:
...
Why: is checks identity, == checks equality. None is a singleton.
4. Comparing Boolean Values Explicitly
Anti-pattern:
if flag == True: # WRONG
...
if len(items) > 0: # WRONG
...
Fix:
if flag:
...
if items:
...
5. Not Using Context Managers for Files
Anti-pattern:
f = open("file.txt") # WRONG - file may not close if error occurs
data = f.read()
f.close()
Fix:
with open("file.txt") as f:
data = f.read()
6. String Concatenation in Loops
Anti-pattern:
result = ""
for item in items:
result += str(item) # WRONG - creates new string each iteration
Fix:
result = "".join(str(item) for item in items)
7. Modifying List While Iterating
Anti-pattern:
for item in items:
if should_remove(item):
items.remove(item) # WRONG - skips elements
Fix:
items = [item for item in items if not should_remove(item)]
# Or
items[:] = [item for item in items if not should_remove(item)]
8. Using eval() or exec()
Anti-pattern:
user_input = get_user_input()
result = eval(user_input) # WRONG - major security risk
Fix:
import ast
result = ast.literal_eval(user_input) # Only evaluates literals
9. Not Using enumerate()
Anti-pattern:
i = 0
for item in items:
print(f"{i}: {item}")
i += 1
Fix:
for i, item in enumerate(items):
print(f"{i}: {item}")
10. Creating Empty Lists/Dicts Unnecessarily
Anti-pattern:
items = []
items.append(1)
items.append(2)
items.append(3)
Fix:
items = [1, 2, 3]
11. Not Using dict.get() with Defaults
Anti-pattern:
if key in my_dict:
value = my_dict[key]
else:
value = default
Fix:
value = my_dict.get(key, default)
12. Using range(len()) Instead of enumerate()
Anti-pattern:
for i in range(len(items)):
item = items[i]
print(f"{i}: {item}")
Fix:
for i, item in enumerate(items):
print(f"{i}: {item}")
13. Not Using Collections Module
Anti-pattern:
word_counts = {}
for word in words:
if word in word_counts:
word_counts[word] += 1
else:
word_counts[word] = 1
Fix:
from collections import Counter
word_counts = Counter(words)
14. Not Using defaultdict
Anti-pattern:
groups = {}
for item in items:
key = get_key(item)
if key not in groups:
groups[key] = []
groups[key].append(item)
Fix:
from collections import defaultdict
groups = defaultdict(list)
for item in items:
key = get_key(item)
groups[key].append(item)
15. Overly Complex Comprehensions
Anti-pattern:
result = [
transform(x)
for x in items
if condition1(x)
if condition2(x)
if condition3(x)
for y in x.sub_items
if condition4(y)
] # WRONG - too complex
Fix:
result = []
for x in items:
if condition1(x) and condition2(x) and condition3(x):
for y in x.sub_items:
if condition4(y):
result.append(transform(x))
16. Not Using Path Objects
Anti-pattern:
import os
path = os.path.join(dir_name, "file.txt")
if os.path.exists(path):
with open(path) as f:
...
Fix:
from pathlib import Path
path = Path(dir_name) / "file.txt"
if path.exists():
with path.open() as f:
...
17. String Formatting with + or %
Anti-pattern:
message = "Hello, " + name + "! You have " + str(count) + " messages."
message = "Hello, %s! You have %d messages." % (name, count)
Fix:
message = f"Hello, {name}! You have {count} messages."
18. Not Using dataclasses
Anti-pattern:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
Fix:
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
19. Lambda Abuse
Anti-pattern:
process = lambda x: x.strip().lower().replace(" ", "_")[:20] # WRONG
Fix:
def process(x: str) -> str:
"""Clean and truncate string."""
return x.strip().lower().replace(" ", "_")[:20]
20. Not Using Sets for Membership Testing
Anti-pattern:
valid_codes = ["A1", "A2", "A3", ...] # Long list
if code in valid_codes: # O(n) lookup
...
Fix:
valid_codes = {"A1", "A2", "A3", ...} # Set
if code in valid_codes: # O(1) lookup
...
Summary
Key principles to avoid anti-patterns:
- Use built-in functions and standard library when possible
- Leverage context managers for resource management
- Use appropriate data structures (sets for membership, Counter for counting)
- Keep code readable and idiomatic
- Use modern Python features (f-strings, dataclasses, Path)
- Avoid premature optimization
- Write explicit, clear code over clever code