Question presented to candidate:
"Here is def add_item(item, bucket=[]): bucket.append(item); return bucket. Call it twice without a bucket. What do you get, why, and how would you fix it?"
What a strong answer should cover:
- Default expressions are evaluated once, when the
defstatement executes, not on every call; the resulting object is stored on the function in__defaults__(or__kwdefaults__) - So a mutable default (
[],{},set(), a custom object) is one shared object; mutations from one call leak into every later call that relies on the default - The fix:
Noneas a sentinel and create the object inside the body; a privateobject()sentinel whenNoneis a valid input;field(default_factory=list)in dataclasses - The same "evaluated once" rule explains
def f(ts=datetime.now())freezing a timestamp, and the class-attribute version of the bug - Linters catch it (Ruff / flake8-bugbear
B006, PylintW0102 dangerous-default-value), and the deliberate exception: a default dict used as a cache, usually better done withfunctools.lru_cache
Code / implementation expected: Yes β reproduce the bug, show __defaults__ changing, then write the None-sentinel version.