DEV Community

Ankur Marwaha
Ankur Marwaha

Posted on

Why Did My Python List Change? Understanding References and Copies

Why Did My Python List Change? Understanding References and Copies

You create a list, assign it to another variable, and change one item. Then you discover that your original list has changed too.

If that has surprised you, the key is understanding what assignment does in Python.

Let’s work through a few examples, starting with a simple shopping list.

1. Assignment does not create a copy

shopping = ["milk", "bread", "eggs"]
backup = shopping

backup.append("coffee")

print(shopping)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

['milk', 'bread', 'eggs', 'coffee']
['milk', 'bread', 'eggs', 'coffee']
Enter fullscreen mode Exit fullscreen mode

Despite its name, backup is not a separate list.

This statement:

backup = shopping
Enter fullscreen mode Exit fullscreen mode

binds another name to the same list. Calling append() through either name changes that shared object.

You can check whether two names refer to the same object using is:

print(shopping is backup)
Enter fullscreen mode Exit fullscreen mode

Output:

True
Enter fullscreen mode Exit fullscreen mode

The lesson: Giving a list another name does not duplicate it.

2. Changing a list differs from reassigning a variable

Now consider this example:

shopping = ["milk", "bread"]
backup = shopping

backup = ["coffee"]

print(shopping)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

['milk', 'bread']
['coffee']
Enter fullscreen mode Exit fullscreen mode

Why did shopping stay unchanged this time?

The statement:

backup = ["coffee"]
Enter fullscreen mode Exit fullscreen mode

creates a new list and makes backup refer to it. It does not modify the original list.

Compare these two operations:

Operation Effect
backup.append("coffee") Changes the list that backup refers to
backup = ["coffee"] Makes backup refer to a different list

When tracing Python code, ask: Does this line change an existing object, or change which object a name refers to?

3. Use a copy when you need a separate list

For a flat list of strings, the copy() method provides the separation we wanted:

shopping = ["milk", "bread"]
backup = shopping.copy()

backup.append("coffee")

print(shopping)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

['milk', 'bread']
['milk', 'bread', 'coffee']
Enter fullscreen mode Exit fullscreen mode

You can also create a shallow copy of a list using a full slice:

backup = shopping[:]
Enter fullscreen mode Exit fullscreen mode

Both approaches create a new outer list. But “shallow” matters when the list contains other lists.

4. A shallow copy can still share nested lists

Suppose you keep scores in a nested list:

scores = [[10, 20], [30, 40]]
backup = scores.copy()

backup[0][0] = 99

print(scores)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

[[99, 20], [30, 40]]
[[99, 20], [30, 40]]
Enter fullscreen mode Exit fullscreen mode

The outer lists are separate, but they contain references to the same inner lists.

We can check both levels:

print(scores is backup)
print(scores[0] is backup[0])
Enter fullscreen mode Exit fullscreen mode

Output:

False
True
Enter fullscreen mode Exit fullscreen mode

Changing an element inside a shared inner list affects what both outer lists display.

However, replacing an entire item in the copied outer list behaves differently:

scores = [[10, 20], [30, 40]]
backup = scores.copy()

backup[0] = [99, 20]

print(scores)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

[[10, 20], [30, 40]]
[[99, 20], [30, 40]]
Enter fullscreen mode Exit fullscreen mode

Here, we replaced a reference in backup. We did not modify the shared inner list.

5. Copy nested data when independence is required

For this nested-list example, deepcopy() also copies the inner lists:

from copy import deepcopy

scores = [[10, 20], [30, 40]]
backup = deepcopy(scores)

backup[0][0] = 99

print(scores)
print(backup)
Enter fullscreen mode Exit fullscreen mode

Output:

[[10, 20], [30, 40]]
[[99, 20], [30, 40]]
Enter fullscreen mode Exit fullscreen mode

Use deep copying when the nested mutable data needs to be independent. It is not necessary for every list: a shallow copy may provide exactly the behaviour you need.

6. Watch out for repeated nested lists

The same reference issue appears when building a grid:

grid = [[0, 0]] * 3

grid[0][0] = 1

print(grid)
Enter fullscreen mode Exit fullscreen mode

Output:

[[1, 0], [1, 0], [1, 0]]
Enter fullscreen mode Exit fullscreen mode

The multiplication repeats references to one inner list. It does not create three independent rows.

Create each row separately instead:

grid = [[0, 0] for _ in range(3)]

grid[0][0] = 1

print(grid)
Enter fullscreen mode Exit fullscreen mode

Output:

[[1, 0], [0, 0], [0, 0]]
Enter fullscreen mode Exit fullscreen mode

The list comprehension creates a new inner list on each iteration.

A useful habit when reading Python code

Whenever a question involves lists, pause at assignments and modifications:

  • Does another variable refer to the same list?
  • Was a shallow copy created?
  • Are any inner lists still shared?
  • Is the code modifying an object or reassigning a name?

Predict the output first, then run the example. Explaining why the result occurs is more useful than memorising it.

For further reading, see the official Python documentation on shallow and deep copying.

Free PCEP practice test

Preparing for PCEP? I create Python learning resources through AM Academy, and you can access my free practice test here: https://www.udemy.com/course/python-pcep-30-03-certification-practice-tests-2026/?couponCode=FREE-COURSE-PCEP. This is an independently created preparation resource, not an official Python Institute exam.

Top comments (0)