PythonMastery

A default list is shared by every call

A default like tags=[] is built once, when the function is defined, and every call shares it. Use None and create the list inside the function.

A default value is created once, when Python reads the def line, not each time the function is called. Make it a list or a dict and every call that relies on the default shares the same object.

The surprise

python
def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("tips"))    # a fresh call... with yesterday's list
output
['python']
['python', 'tips']

The fix

Default to None, and build the list inside the function, where it runs on every call.

python
def add_tag(tag, tags=None):
    if tags is None:
        tags = []
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("tips"))
output
['python']
['tips']

Why it works

The default lives on the function object itself. You can watch it change:

python
def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

add_tag("a")
add_tag("b")
print(add_tag.__defaults__)
output
(['a', 'b'],)

None can't be changed in place, so there is nothing to share between calls. The if tags is None check runs every time and hands each call its own list.

When not to worry

Defaults that can't change in place (numbers, strings, tuples, None) are always safe. And if you want a function to remember results between calls, don't lean on this quirk: say so with functools.cache.

Learn it properly: Functions & Scoping