Find the bug · Easy

Fix the Mutable Default Argument

Easy Find the bug Most asked

collect_evens(nums) returns the even numbers of nums in order. It takes an optional result list so callers can append into an existing list.

The first call works. Later calls return numbers from earlier calls too. Find the bug and fix it without removing the optional parameter.

Example 1

Input: collect_evens([1, 2, 3, 4])
Output: [2, 4]

Example 2

Input: collect_evens([6, 7])  # called next
Output: [6]
Explanation: The buggy version returns [2, 4, 6].

The buggy code

def collect_evens(nums: list[int], result: list[int] = []) -> list[int]:
    for x in nums:
        if x % 2 == 0:
            result.append(x)
    return result

Hints

Hint 1

When is a default argument value created in Python: on each call, or once?

Find and fix the bug

Loading the editor… (JavaScript is required to run code in your browser.)

Solution and explanation

Show the bug and the fix

Time O(n) · Space O(n)

The bug: default values are evaluated once, when the def statement runs, not on every call. result: list[int] = [] creates a single list shared by every call that omits the argument, so each call appends to the evens from all previous calls.

The fix: use None as the sentinel and create a new list inside the function: if result is None: result = []. The same trap applies to {} and set() defaults, and to dataclass fields (use field(default_factory=list)).

Fixed version

def collect_evens(nums: list[int], result: list[int] | None = None) -> list[int]:
    if result is None:
        result = []  # a new list on every call
    for x in nums:
        if x % 2 == 0:
            result.append(x)
    return result

Common mistakes

  • "Fixing" it by clearing the list at the start of the function, which breaks callers who pass their own list.