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 None as 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?