In Python, default arguments are evaluated only once when the function is defined, not each time the function is called. This means that if a mutable object (like a list or a dictionary) is used as a default value for a parameter, it will be shared across all calls to the function where this argument is not specified.
Example:
def append_item(item, items=[]): items.append(item) return items print(append_item(1)) # [1] print(append_item(2)) # [1, 2], but we would expect [2]
The correct way:
def append_item(item, items=None): if items is None: items = [] items.append(item) return items
Now each call gets its own list.
Question: What happens when calling the function multiple times with a mutable object as a default value?
Answer: The same object is modified each time. The example above illustrates this — the list accumulates all the values.
Story In a large web application, when caching data, they used a function with a default dictionary parameter. This resulted in data "leaking" between different users: someone changed their profile — and those changes sometimes appeared to another user due to the shared state of the global dictionary.
Story In tests, they used a function with a mutable list as a default for collecting statistics. Data from one test "flowed" into another, leading to unexpected crashes, an inability to reproduce bugs, and complicated debugging.
Story In a microservice for aggregating logs, they accumulated events using a function where the default argument was a list. Logs were duplicated — temporary accumulation from old requests affected new clients, costing hours of investigation and data loss.