Back to Python & Coding

PYTHON & CODING · QUESTION 76

Why are mutable default arguments risky in Python?

On this page
Intermediate 4 min read
Interview-ready answerThe concise version to remember

Python evaluates default argument expressions once when a function is defined, not each time it is called. If a default is a mutable object such as a list, calls can share and modify the same object unexpectedly. Use `None` as the default and create a fresh list or dictionary inside the function when no value is supplied.

LET'S MAKE IT SIMPLE

First, the idea in plain English

A function definition is created once and its default values stay attached to it. A list can be changed, so if the same default list is reused, one call may affect later calls. This surprises people who expect a new empty list every time the function runs.

A USEFUL COMPARISON

Picture it this way

Imagine a shared whiteboard used as the “default notes” for every meeting. Changes from one meeting remain for the next. Creating a fresh board inside each meeting avoids the shared state; Python’s default mutable object behaves like the shared board.

FOLLOW THE JOURNEY

A mutable default object is shared across calls

Compare definition-time default creation with per-call initialization.

A mutable default object is shared across callsCompare definition-time default creation with per-call initialization.Code behavior comparison
01Define functionPython evaluates the default expression when it executes the definition.
02Call without argumentThe function receives the same stored object.
03Initialize per callUse None and create a new collection inside the function.

This simplified visual highlights the main relationships. In practice, the exact design depends on the requirements; Each call then gets independent state.

A function definition stores one default list that multiple calls mutate, while a None sentinel creates a fresh list inside each call.

How to read this visual
This simplified visual highlights the main relationships. In practice, the exact design depends on the requirements; Each call then gets independent state.

UNDER THE HOOD

How it works, step by step

01

Define function

Python evaluates the default expression when it executes the definition.

Why it matters: The object becomes part of the function’s stored defaults.

02

Call without argument

The function receives the same stored object.

Why it matters: A mutable value can retain earlier changes.

03

Initialize per call

Use None and create a new collection inside the function.

Why it matters: Each call then gets independent state.

OUTSIDE THE TEXTBOOK

A practical example

A helper `append_item(item, items=[])` appends to one shared list, so a later call without `items` sees previous items. Define `items=None`, then assign `items = [] if items is None else items` inside the function to make the default behavior fresh.

GO A LITTLE DEEPER

Technical details that matter

  • Default expressions are evaluated in the defining scope at function-definition time.
  • Immutable defaults such as integers and strings do not have the same mutation issue, though mutable state can still be nested inside custom objects.
  • Intentional shared caches can use mutable defaults, but explicit state or a cache decorator is clearer.
Python example
def append_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

The None sentinel distinguishes “no list supplied” from an explicitly supplied list. A new list is allocated only for calls that omit the argument.

MAKE A GOOD CHOICE

When is this useful?

  • Writing helpers that accept optional collections.
  • Reviewing unexpected state leaking between function calls.
  • Understanding function objects and definition-time evaluation.
Consider the alternative

Use a factory or explicit object when the function needs intentional persistent state. Avoid mutable defaults for per-call data.

WATCH OUT FOR

Common misunderstandings

  • Using `[]` or `{}` as a default and mutating it.
  • Replacing an explicitly supplied empty list because it is falsey.
  • Assuming default expressions run on every call.

REAL-WORLD TRADE-OFFS

Limitations and failure cases

  • The None pattern requires a sentinel if None is also a meaningful explicit value.
  • Hidden state may still be introduced through globals or object attributes.

WHAT TO REMEMBER

Key takeaways

  1. 1Defaults are evaluated once when the function is defined.
  2. 2Mutable defaults can persist mutations between calls.
  3. 3Use a sentinel and create fresh mutable state inside the function.

PRACTICE THE FOLLOW-UP

What might an interviewer ask next?

Is an empty tuple safe as a default?

A tuple itself is immutable, so it cannot be appended to. But an immutable tuple containing mutable objects can still expose shared mutable state.

Why not use `items = items or []`?

That replaces any explicitly supplied empty list because it is falsey. Checking `items is None` preserves the caller’s object.

SAY IT OUT LOUD

How to explain this in an interview

“Python evaluates default arguments once, when the function is defined. If that default is a list and the function mutates it, later calls share the changes. I use None as a sentinel and create a fresh list inside the function when needed. I check with `is None` so an explicitly supplied empty list remains intact.”

Continue your learning path

Connect this question to the roadmap topic it supports.

Open roadmap topic