Mutable Default Argument
core
Implement log_event(name, log=None): append name to log and return the list. When the caller passes no log, start a fresh, empty list; every bare call is its own independent one-entry log. The grader calls log_event many times in a single run, always without the second argument, and expects each call to return [name] alone.
The drill is the default value itself. def log_event(name, log=[]) evaluates the [] exactly once, at def time, and stores that single list object on the function; every call that omits log shares it. The first bare call looks perfect (one append into an empty list), which is what makes this trap survive code review. From the second call on, the shared list still holds every earlier entry, and each call's "fresh log" quietly accumulates the whole history.
The idiom is a None sentinel. Default to None, and rebind with if log is None: log = [] inside the body: now the empty list is created at call time, once per bare call, and callers who do pass a list still get appended to in place. The same def-time rule bites {}, set(), and any other mutable default; None is the standard escape for all of them.
Example: three bare calls log_event('a'), log_event('b'), log_event('c') must return ['a'], ['b'], ['c']. With log=[] in the signature they return ['a'], ['a', 'b'], ['a', 'b', 'c'], the same list object growing across calls.
Input format: each test in tests/*.in is laid out as:
- Line 1:
n, the number of calls. - Next
nlines: one event name per line; lineibecomes the bare calllog_event(name)numberi.
Test 03.in reads:
5
tick
tock
tick
tock
tick
This is five bare calls with repeating names: each must come back as its own one-entry log, ['tick'] alone every time that name recurs.
Where you'll use it: any recursive helper written as def dfs(node, path=[]) fails exactly this way on its second top-level invocation. Collector-style solutions to these are the usual victims:
✦ Solution & editorial unlock with the pass.