Jun Jie

Mutable Default Arguments in Python

Explaining a common pitfall when using Python's default function arguments.

September 2, 2025 · min read


Recently, I made a beginner mistake when it comes to using mutable data structures (lists, dictionaries, etc.) in default arguments.

Take this code snippet as an example:

from typing import List

def func(a: List[str] = []) -> List[str]:
    return a

arr1 = func() # arr1 = []
arr2 = func() # arr2 = []

arr1.append('a')

print(f'arr1: {arr1}')
print(f'arr2: {arr2}')

Output:

arr1: ['a']
arr2: ['a']

Why did mutating arr1 affect arr2? Shouldn't they be different list instances?

Why?

Looking at the Python docs at 8.7. Function definitions, it states: the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.

This behaviour works with primitive types, but unexpected errors occur when using mutable objects, since the same object instance is used for every function call, instead of a new instance being created.

Thus, the output is attributed to arr1 and arr2 sharing the same list instance.

The Fix

To use mutable objects as default arguments in Python, we can use the primitive None as the default value, then explicitly test for it:

from typing import Optional, List

def func(a: Optional[List[str]] = None) -> List[str]:
    if a is None:
        return []
    return a

arr1 = func() # arr1 = []
arr2 = func() # arr2 = []

arr1.append('a')

print(f'arr1: {arr1}')
print(f'arr2: {arr2}')

Output:

arr1: ['a']
arr2: []

The pattern above, ensures that a new object instance is created every time the function is called.

And if there's one thing to remember, it is to prevent using mutable data types directly as default arguments.

References