Files
gh-codingkaiser-claude-kais…/skills/python-style-guide/references/antipatterns.md
2025-11-29 18:15:04 +08:00

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:

  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