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