본문 바로가기

Why Mutable Default Arguments Are Structurally Dangerous in Python

@PyVeeDeeO2025. 12. 20. 23:46

Why Mutable Default Arguments Are Structurally Dangerous in Python

Mutable default arguments are one of the most persistent sources of surprise in Python. A function appears to start with a clean default value, yet state leaks across calls.

The behavior looks like a bug. Each invocation feels independent. The function body does not obviously store global state.

Despite this, lists grow unexpectedly. Dictionaries retain old keys. Data from previous calls reappears without any explicit reference.

This is not a corner case. It follows directly from how Python creates function objects and binds default values.

 

python-mutable-defaults

 

Short Summary

Mutable default arguments in Python persist across function calls because they are created once when the function object is defined.
This causes unintended state sharing between invocations, making behavior appear non deterministic and hard to trace.
The issue is structural, rooted in function creation semantics, not a quirk of argument passing or runtime execution.

 

 

 

Summary Card

Mutable Defaults and Hidden State

  • Default argument values are created once, at function definition time.
  • Mutable defaults are shared across all calls to the function.
  • The function object, not the call, owns the default value.
  • This sharing creates implicit state that persists between invocations.
  • The behavior is structural, not a runtime accident.

Problem definition

A function defines a mutable object as a default parameter.

On the first call, the function behaves as expected. On later calls, the default value already contains data.

The caller did not pass any argument. The function signature looks pure. Yet state has accumulated.

This pattern frequently leads to data corruption, unexpected coupling between calls, and hard to reproduce bugs.

The common wrong assumptions

The most common assumption is that default arguments are evaluated at call time.

Another assumption is that each call receives a fresh instance of the default value.

Both assumptions are incorrect. In Python, defaults belong to the function object itself.

Once this distinction is missed, the behavior looks inexplicable.

What actually happens inside Python

In CPython, a function is an object created when the def statement executes.

At that moment, default argument expressions are evaluated and stored inside the function object.

These values are kept in the function’s internal default storage and reused for every call.

If a default value is mutable, mutations affect the single shared object.

No new object is created on subsequent calls. The same memory is reused by design.

The execution model is consistent. The surprise comes from assuming defaults are part of call semantics rather than object construction.

 

Why Python is designed this way

Evaluating defaults at function creation simplifies the language and runtime.

Default values become immutable parts of the function object’s signature.

This avoids repeated evaluation overhead and keeps call execution fast.

The tradeoff is that mutable objects introduce shared state unless explicitly avoided.

Anti patterns that make it worse

Using mutable defaults as implicit caches creates hidden global state.

Relying on defaults for accumulating results across calls blurs function boundaries.

Assuming defaults reset between invocations leads to subtle data leaks.

Stable design strategies

Treat default arguments as immutable configuration, not storage.

When per call state is required, create it inside the function body.

Design functions so repeated calls are independent by construction.

The key decision is whether state belongs to the function object or to each invocation.

Conclusion

Mutable default arguments are dangerous because they introduce shared state by design.

The behavior follows directly from function object creation semantics.

It is deterministic, predictable, and often misunderstood.

Once the ownership of defaults is clear, the risk becomes obvious.

 

Related posts

  • Why Everything Is an Object in Python
  • When Reference Count Changes Break Intuition in Python
  • Why Python Garbage Collection Looks Like It Is Not Running
  • Why Python Integers Are Immutable

python-mutable-defaults

PyVeeDeeO
@PyVeeDeeO :: PyVeeDeeO

PyVeeDeeO

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차