Python Copying Lists

In the last lesson, we learned about Sorting Python Lists. Now let’s copy a list without accidentally changing the original.

Let’s Talk About Copies

We have a list. And we want to make a copy of it. So we can change the copy without changing the original.

Seems simple, right? Just do new_list = old_list.

But that does not work the way we think. That does not make a copy. That just gives the same list another name.

Let me show.

original = [1, 2, 3]
new_list = original
new_list.append(4)
print(original)

Output

[1, 2, 3, 4]

Wait. Why did original change? I only changed new_list.

Because new_list = original does not copy the list. It just makes new_list point to the same list as original. They are two names for the same object.

Think of it like this. We have a house. We give our friend the address. Now we both can go to the same house. If our friend paints the door red, the door is red for us too. Because it is the same house.

That is what happens with new_list = original. They both point to the same list. So changes through one name are visible through the other.

If we want a real copy, we need to do something else.

The copy() Method in Python

Lists have a copy() method. It creates a shallow copy of the list.

original = [1, 2, 3]
new_list = original.copy()
new_list.append(4)
print(original)
print(new_list)

Output

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

Now original is unchanged. Because new_list is a separate list.

That is a shallow copy. For simple lists, that is all we need.

Other Ways to Make a Shallow Copy in Python

We can also use the list() function.

original = [1, 2, 3]
new_list = list(original)

Or slicing.

new_list = original[:]

All three do the same thing: they create a new list with the same items.

The Shallow Copy Problem in Python

A shallow copy works fine when our list contains only simple things like numbers, strings, or booleans. Those are immutable. So copying them is safe.

But if our list contains mutable objects — like other lists — then a shallow copy is not enough.

Let me show.

original = [[1, 2], [3, 4]]
new_list = original.copy()
new_list[0][0] = 99
print(original)
print(new_list)

Output

[[99, 2], [3, 4]]
[[99, 2], [3, 4]]

Wait. I changed only new_list, but original changed too.

Why? Because a shallow copy copies the outer list, but not the inner lists. The inner lists are still shared between the two copies.

Think of it like this. We have two houses. But they share the same furniture. If we move a chair in one house, it moves in the other too.

That is a shallow copy. It copies the container, but not the things inside.

Deep Copy in Python

To truly copy a nested list, we need a deep copy. That copies everything, all the way down.

Python has a module called copy that provides deepcopy().

import copy
original = [[1, 2], [3, 4]]
new_list = copy.deepcopy(original)
new_list[0][0] = 99
print(original)
print(new_list)

Output

[[1, 2], [3, 4]]
[[99, 2], [3, 4]]

Now original is unchanged. Because deepcopy() created completely separate inner lists.

That is what we want when we have nested structures.

When to Use Shallow vs Deep Copy in Python

Use shallow copy when:

  • Our list contains only immutable items (numbers, strings, tuples).
  • We do not plan to modify the inner objects.
  • We want a quick copy.

Use deep copy when:

  • Our list contains mutable items (other lists, dictionaries).
  • We plan to modify the inner objects independently.
  • We need a truly independent copy.

Deep copy is slower and uses more memory. So let’s not use it unless we need it.

Python Copying Lists: A Real-Life Example

Let’s say we have a list of students, and each student is a list of name and grades.

students = [
["Alex", [85, 90]],
["Sam", [92, 88]]
]
# Make a shallow copy
shallow = students.copy()
shallow[0][1].append(95)
print("Original:", students)
print("Shallow:", shallow)

Output

Original: [['Alex', [85, 90, 95]], ['Sam', [92, 88]]]
Shallow: [['Alex', [85, 90, 95]], ['Sam', [92, 88]]]

The inner list of grades changed in both. Because the shallow copy shared the inner list.

Now with deep copy:

import copy
students = [
["Alex", [85, 90]],
["Sam", [92, 88]]
]
deep = copy.deepcopy(students)
deep[0][1].append(95)
print("Original:", students)
print("Deep:", deep)

Output

Original: [['Alex', [85, 90]], ['Sam', [92, 88]]]
Deep: [['Alex', [85, 90, 95]], ['Sam', [92, 88]]]

Now the original is untouched. Because deep copy made everything independent.

Common Mistakes with Python Copying Lists

Mistake 1: Using = for Copying

a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]

So what do we do? We use a.copy(), list(a), or a[:].

Mistake 2: Using Shallow Copy on Nested Lists

a = [[1, 2], [3, 4]]
b = a.copy()
b[0][0] = 99
print(a) # [[99, 2], [3, 4]]

So what do we do? We use copy.deepcopy(a).

Mistake 3: Forgetting to Import copy

import copy
new_list = copy.deepcopy(original)

We must import the module first.

Mistake 4: Thinking Deep Copy Is Always Needed

Deep copy is slower. If we only have simple items, shallow copy is enough. Let’s not overuse deep copy.

A Quick Note on Tuples in Python

Tuples are immutable. So we do not need to copy them. They cannot be changed. So sharing is safe.

Next, we will learn about Python Tuples and store ordered values that cannot be changed.