You might expect each call to start with an empty list. It doesn’t.
Python evaluates default argument expressions when the function is defined, not each time it’s called. Both calls reuse the same list object because neither supplies an explicit items argument.
The list isn’t recreated between calls. It’s quietly keeping receipts.
Now the function creates a new list whenever the caller omits items. If the caller explicitly provides a list, that list is still modified.
That distinction is important: the function doesn’t promise never to mutate a supplied list. It only avoids accidentally sharing its default list between calls.
The important nuance: Mutable defaults aren’t always wrong
A default argument that intentionally retains state can be useful in specialized designs. The problem is accidental shared state, especially with lists, dictionaries, and sets.
For dataclasses, there’s a related pattern worth knowing. Use default_factory when each instance needs its own mutable default:
from dataclasses import dataclass, field
@dataclassclassShoppingList:
items: list[str] = field(default_factory=list)
Each ShoppingList instance receives a list created by the factory rather than sharing one list across instances.