A list that keeps growing between function calls is one of the most common Python surprises in exams and interviews. Here is why it happens, with output I ran on Python 3.10.

The bug

def add_bad(x, bucket=[]): bucket.append(x) return bucket print(add_bad(1)) print(add_bad(2)) print(add_bad(3)) 

Output:

[1] [1, 2] [1, 2, 3] 

You expected [1], [2], [3]. Python builds the default value once, when def runs, not on each call. Every call that skips bucket reuses the same list. You can see it:

print(add_bad.__defaults__) # ([1, 2, 3],) 

The fix

Use None as the default and create the list inside the function.

def add_ok(x, bucket=None): if bucket is None: bucket = [] bucket.append(x) return bucket print(add_ok(1)) # [1] print(add_ok(2)) # [2] 

Same family: shallow copies

list.copy() copies only the outer list. The inner lists are shared.

import copy a = [[1, 2], [3, 4]] b = a.copy() b[0].append(99) print(a) # [[1, 2, 99], [3, 4]] a changed too  c = copy.deepcopy(a) c[0].append(5) print(a, c) # a is unchanged by c 

Same family: the grid trap

grid = [[0] * 3] * 2 grid[0][0] = 7 print(grid) # [[7, 0, 0], [7, 0, 0]] both rows are one list  grid2 = [[0] * 3 for _ in range(2)] grid2[0][0] = 7 print(grid2) # [[7, 0, 0], [0, 0, 0]] 

[x] * n repeats a reference to the same object. A list comprehension builds a new row each time.

Quick rule

  • Never use a list, dict or set as a default argument. Use None.
  • copy() and slicing are shallow. Use copy.deepcopy for nested data.
  • Build 2D grids with a comprehension, not *.

Try to predict the output of each snippet before you run it. That habit is what exam questions test.

I wrote a longer version with 7 more tested examples and exam-style questions here: Python Mutable Default Arguments and Shallow Copies. It is part of TechVani, a free BTech CSE notes and lab-files site.