Anti Patterns

(Collection of examples of anti-patterns in Python)

Understanding and avoiding anti-patterns is crucial for writing clean, maintainable, and efficient Python code.

Not using with to open files

Example
def bad_open():
    file = open("myfile.txt", "r")
    content = file.read()
    file.close()
# --- vs ---
def good_open():
    with open("myfile.txt", "r") as file:
        content = file.read()

Overusing list/dict/set comprehensions

Example
filtered_users = {user['name']: user for user in users if user['age'] > 18}
# This comprehension builds a dictionary based on age filtering, but it's less clear than:
filtered_users = {}
for user in users:
    if user['age'] > 18:
        filtered_users[user['name']] = user

Unnecessary use of generators

Example
def count_letters(text):
  return sum(1 for char in text if char.isalpha())  # Generator for simple counting
# --- vs ---
def count_letters(text):
  count = 0
  for char in text:
    if char.isalpha():
      count += 1
  return count  # Direct counter

Returning multiple object types from a function

Example
def get_user_info(user_id):
    if user_id in active_users:
        return active_users[user_id]  # Returns a dictionary
    else:
        return None  # Returns NoneType
# This function might return a dictionary or None, making it less predictable and harder to use.
def get_user_info(user_id):
    user = active_users.get(user_id)  # Use .get() for consistent return type
    return user or {}  # Return an empty dictionary if user not found

Not using get() for default values in dictionaries

Example
data = {"name": "Alice"}
if "age" in data:
  age = data["age"]
else:
    age = 30  # Hardcoded default value
# --- vs ---
data = {"name": "Alice"}
age = data.get("age", 30)  # Default value with `get()`

Not using items() to iterate over dictionaries

Example
user = {"name": "Alice", "age": 30}
for key in user:
  value = user[key]
  # Process key and value
# --- vs ---
user = {"name": "Alice", "age": 30}
for key, value in user.items():
  # Process key and value

Pushing debugger in production code

Example
def my_function():
    critial_data = get_critical_data()
    process_data(critial_data)
    print(critial_data)  # Debugger statement
# --- vs ---
import logging
def my_function():
    critial_data = get_critical_data()
    process_data(critial_data)
    clean_data = process_data(critial_data)
    location = save_artifact(clean_data)
    logging.info(f"Processed data: {clean_data.id} @ {location}")  # Logging statement

God classes

Improved Practices

“It might seem impressive at first, but like a house of cards, one small change can bring the whole thing tumbling down. Refactoring becomes a religious experience.”

Magic numbers

Example
# Use with clear constants:
def calculate_discount(price, discount_threshold=10):
  if price > discount_threshold:  # Hardcoded magic number
    return price * 0.8  # Another magic number
  else:
    return price
# --- vs ---
DISCOUNT_THRESHOLD = 10  # Defined constant
DISCOUNT_RATE = 0.8  # Defined constant

def calculate_discount(price):
  if price > DISCOUNT_THRESHOLD:
    return price * DISCOUNT_RATE
  else:
    return price

Additional Resources

Cargo cult programming anti-pattern

✨ Bonus for reading all of this! ✨

Example

Read more: Cargo cult programming