A function with a list as its default argument can appear to remember earlier calls. Nothing is cached by accident. Python creates the default value once, when the def statement runs, and every call that omits the argument receives that same object. This post shows the rule in the language reference, what it does to a mutable default, and the standard fix.
What the reference says
The Python language reference states that default parameter values are evaluated from left to right when the function definition is executed. It spells out the consequence: the expression is evaluated once, when the function is defined, and the same “pre-computed” value is used for each call.
A def statement is executed like any other statement. It builds a function object, and at that moment it evaluates each default expression and stores the result on the function. The data model reference lists the attribute that holds them: __defaults__, a tuple of default parameter values. A call that leaves the parameter out binds it to the object in that tuple. It does not run the expression again.
The shared list
For immutable defaults such as numbers, strings or None, this is invisible. A mutable default is different, because a call can change the stored object:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a"))
print(add_item("b"))
print(add_item.__defaults__)
Under Python 3.12 this prints:
['a']
['a', 'b']
(['a', 'b'],)
The second call never asked for "a", yet it received it. Both calls appended to the one list that def created, and __defaults__ shows that list with both items in it. The Python tutorial gives the same example with numbers and calls it an important warning: the default value is evaluated only once, which matters when the default is a mutable object such as a list, a dictionary, or an instance of most classes.
The None sentinel
The fix the reference and the programming FAQ both recommend is to use None as the default and create the new object inside the body:
def add_item_fixed(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item_fixed("a"))
print(add_item_fixed("b"))
This prints ['a'] and then ['b']. The body runs on every call, so each call that omits items gets a fresh list. Test with is None rather than a truth test: if not items would also replace an empty list the caller passed on purpose, and the caller would never see the items appended to it.
When sharing is the point
The FAQ notes that the behavior can be useful. Its example keeps a memo cache in a keyword-only default, _cache={}, so results survive between calls. That is a deliberate use of the same rule, and the leading underscore and keyword-only position signal that callers should not pass it.
The rule
Treat a default value as part of the function object, created once at definition time. Use immutable defaults, or use None and build the mutable object inside the body. Keep a shared mutable default only when you want the sharing, and name it so readers can see that.

The Campfire
CommentsThanks. Your comment is waiting for the desk. Once it is approved it appears here within about a minute.
Add a comment