Q4: Why are mutable default arguments (e.g. def fn(arg=[])) considered dangerous?
Default argument values are evaluated once when the function definition is executed at module import time, NOT on each function invocation. Passing mutable default arguments ([] or {}) causes all subsequent calls to mutate the exact same shared object instance.
Safe Default Argument Pattern
# ❌ DANGEROUS: Shared list reference across invocations
def append_to(element, target=[]):
target.append(element)
return target
# ✅ SAFE PATTERN: Re-instantiate mutable inside call
def safe_append(element, target=None):
if target is None:
target = []
target.append(element)
return target💡 Re-assigning target = [] inside function scope creates fresh list allocations per call.
Key Takeaways
- Default parameter objects bind at function definition time.
- Use
Noneas default placeholder and assign new mutables inside the body.
Knowledge Check
What is returned by append_to(1) followed by append_to(2) using the dangerous pattern above?