Skip to main content
Interviewpedia

Why is a mutable default argument risky in Python?

Technical · Fresher

Answer guide

Read the answer guide

Python evaluates default argument expressions once, when the def statement runs, not on every call. So a default like an empty list becomes one shared object, and appending to it in one call changes what the next call sees. A small example is a function that adds an item to a default list and returns it; calling it twice gives a list with both items. The usual fix is to default to None and create a fresh list inside the body when None is received. Numbers and strings do not expose mutable state. A tuple can contain a mutable object such as a list, so a tuple default is safe from shared mutation only when its contents are also immutable. Reviewers often catch this with a linter rule, and a two-call unit test shows the bug clearly.

What the interviewer is assessing

Whether you know when Python evaluates defaults and can spot shared-state bugs early.

Common mistakes

  • Thinking a new default list is created on every call, so the bug is unexpected.
  • Fixing it by copying the list after use instead of using a None sentinel.

Practise a follow-up

  • Is a default tuple or string argument safe, and why?
  • How can a mutable default ever be useful on purpose, for example for caching?
Practise this question →

Worked example: defaults and shared state

Run this with Python 3. Defaults are created at definition time. The tuple example shows why an immutable outer container does not guarantee immutable contents.

def add_bad(item, items=[]):
    items.append(item)
    return items

print(add_bad("a"))
print(add_bad("b"))

def add_good(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_good("a"))
print(add_good("b"))

def add_tuple(item, holder=([],)):
    holder[0].append(item)
    return holder

print(add_tuple("a"))
print(add_tuple("b"))

Expected output

['a']
['a', 'b']
['a']
['b']
(['a'],)
(['a', 'b'],)

Use the None sentinel when omitted arguments should produce independent lists. Passing an explicit list still deliberately shares that caller-owned object.

Source: Python documentation: default argument values.