Default Mutable Arguments

ID

python.default_mutable_arguments

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Python

Tags

reliability, suspicious-construct

Description

Reports parameters with mutable default values ([], {}, set(), list(), dict()). Python evaluates default arguments once at function definition time, so the same mutable object is shared across every call. The first time the function mutates the default, every subsequent call observes the mutation.

def append(item, items=[]):              # FLAW
    items.append(item)
    return items

# After:
#   append(1)        -> [1]
#   append(2)        -> [1, 2]   (!)
#   append(3)        -> [1, 2, 3]

def append(item, items=None):            # OK
    if items is None:
        items = []
    items.append(item)
    return items

Rationale

This is one of the most-cited Python bugs in introductory courses, and yet still slips through review when a function is added quickly. Static detection catches it before the side effect ever fires.

Remediation

Use None as the sentinel and create the mutable inside the body:

def append(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

Immutable defaults (int, str, tuple, frozenset, None) are fine because the shared instance cannot be mutated.