BXUP Track progress & test yourself — free
War Room · Field diagnosis · ~2 min read
WAR ROOM · FIELD DIAGNOSIS

WR-008 · "Why did both lists change?" — aliasing and the mutable default

Symptom

You copied a list with b = a, changed b, and the "original" a changed too. Or a function with a default like def add(row, rows=[]) keeps returning records from earlier calls, as if it remembers the past. No error appears anywhere — the program runs happily and produces wrong data.

Context

Hits when a script starts keeping backup copies of data, or when a helper function grows an optional list/dict argument. Classic moment: "I saved a copy of the clean records before filtering, but the copy got filtered too." Common in exercise code, interview problems, and real cleaning scripts alike. Because nothing crashes, it is often discovered days later from wrong totals. Linked module: 0.3 (Python that AI actually uses).

Diagnosis

Assignment in Python never copies an object — it copies a reference, so b = a makes two names for ONE list. Any change through either name shows through both; this is aliasing. The mutable default is the same disease at the definition line: Python builds the default list ONCE, when the function is defined, and every call without that argument shares that single list, so appends pile up across calls. Confirm with identity, not appearance: print(id(a), id(b)) — equal ids means one object with two names. a is b returning True says the same. For the default: call the function twice with no list argument; if the second result contains the first call's items, you have the shared default.

The fix

For aliasing: make a real copy at the assignment — b = list(a) or b = a.copy(). Note these copy one level only; a list of dicts still shares the inner dicts, and copy.deepcopy(a) is the full-depth copy when you truly need one. For the mutable default: use the None pattern —

def add(row, rows=None): then inside, if rows is None: rows = [].

This builds a fresh list per call. Re-run the identity check afterwards: a is b must now be False, and two bare calls must return independent results.

Prevention

Say the rule aloud when you type =: names point at objects; assignment shares, .copy() copies. Never write a list or dict literal as a default argument — the None pattern is the professional reflex, and code reviewers reject rows=[] on sight. When a value changes "by itself", reach for id() or is first: identity evidence beats staring at code. Comprehensions help too — building a new list at each step leaves nothing shared to mutate.

In an interview

Q: "What does this print?" followed by the b = a; b.append(...) snippet, or "what is wrong with def f(x, items=[])?" — both are top-ten Python interview questions in Indian service and product companies. Strong answer: state that assignment copies references not objects, predict the shared-change output confidently, explain the one-time creation of default values, and give both fixes (list(a)/copy(), and the None pattern) with the one-level limit of shallow copies. Bonus credit for is and id() as the confirming tools.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in