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
- Validate inputs at function boundaries.
- Use guard clauses to exit early on invalid states.
- Avoid mutable default arguments.
- 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
pydanticorattrsfor 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.