What are defensive programming patterns in Python?

· Category: Python Programming

Short answer

Defensive programming anticipates problems and handles them gracefully. In Python, this means validating inputs, using guard clauses, avoiding mutable defaults, and providing sensible fallbacks.

Steps

  1. Validate inputs at function boundaries.
  2. Use guard clauses to exit early on invalid states.
  3. Avoid mutable default arguments.
  4. Provide fallback values and log warnings instead of crashing when possible.
def process_user(user):
    if not user:
        return None
    if not isinstance(user, dict):
        raise TypeError("user must be a dict")
    name = user.get("name", "Unknown")
    age = user.get("age")
    if age is not None and not isinstance(age, int):
        raise ValueError("age must be an int")
    return {"name": name, "age": age}

# Safe fallback
def get_config(key, default=None):
    config = {"timeout": 30}
    return config.get(key, default)

Tips

  • Fail fast at the boundaries of your system (API endpoints, file readers, user inputs).
  • Use pydantic or attrs for automatic validation of complex data structures.
  • Logging warnings helps detect misuse without breaking running systems.
  • Unit tests should cover both happy paths and invalid inputs.

Common issues

  • Swallowing all exceptions with a bare except: hides bugs and makes debugging impossible.
  • Over-validation inside hot loops hurts performance; validate once and pass trusted data inward.
  • Mutable default arguments cause shared state across calls when not guarded.