How to Copy a List in Python? (Why It Fails with ‘=’ Operator)

In Python, copying a list can be unintuitive for beginners.

This is because you cannot use the assignment operator to take an independent copy of a list.

To create an independent copy of a list in Python, you need to use the copy module’s deepcopy() function like this:

import copy

new_list = copy.deepcopy(old_list)

This comprehensive guide teaches you how to take an independent copy of a list in Python.

You will also learn why doing it with an assignment operator doesn’t work.

The Problem with Copying Lists in Python

When dealing with Python variables, you can create a copy of a variable by assigning the existing variable to a new one.

For example:

a = 10
b = a

Now the variable b is a copy of variable a.

But if you copy a Python list in a similar fashion:

new_list = old_list

Any modifications made to the new_list also change the original list old_list.

It happens because new_list is actually not a copy of old_list. Instead, it is a reference to the same object in memory.

“Copying” a list just creates an alias for the same object in memory.

To create a completely independent copy of a list, use the copy module’s deepcopy() function.

import copy

new_list = copy.deepcopy(old_list)

This is the only rational way to create a fully independent copy of a list in Python.

But if you don’t care about independent copies, there are many other ways to create a copy of a list in Python. These are:

  1. copy() method. Creates a shallow copy.
  2. [:] slicing operator. Creates a shallow copy.
  3. list() function. Creates a shallow copy.
  4. copy.copy() function. Creates a shallow copy.
  5. copy.deepcopy() function. Creates a deep copy.

The following sections teach you how the assignment operator works, what is a shallow copy, and why copying “fails” with the assignment operator.

Assignment Operator (=) in Python

If you use the assignment operator (=) to copy a list in Python, you are not actually creating a copy of the object. Instead, you just give rise to a new variable that refers to the original list. The new variable is an alias to the original list.

Let’s see an example where we:

  1. Create a list.
  2. Assign or “copy” the list to a new variable.
  3. Change the first number in the original list.
  4. Print both lists.

And see what happens.

Here’s the code:

numbers = [1, 2, 3]
new_numbers = numbers

# Only change the original list
numbers[0] = 100

print(numbers)
print(new_numbers)

Output:

[100, 2, 3]
[100, 2, 3]

Here you only changed the first element in the original numbers list. But this change also took place in the new_numbers list.

This happens because numbers and new_numbers are actually the very same list object.

Python variables pointing to the same memory block when assigned to new variable
Under the hood, both lists point to the same blob in memory.

Another way to verify this is by checking the memory address of these objects.

In Python, you can use the id() method to find out the memory address of any object.

Let’s check the memory addresses of both numbers and new_numbers.

print(id(numbers))
print(id(new_numbers))

Output:

140113161420416
140113161420416

The IDs are the same!

This verifies that the numbers and new_numbers are aliases pointing to the same list object in memory.

Think of the list object as a chunk of memory without a name. The numbers and new_numbers are just names via which you can access the list object in memory.

So when you create a new variable and assign a list object to it, you are introducing a new reference label to the original object.

To recap, the assignment operator (=) creates a new reference to an object in memory. It does not copy anything. This applies to lists as well as any other object in Python.

Next, let’s take a look at how you can actually copy list objects in Python.

The Copy Module in Python

As you learned, you cannot use the assignment operator to copy objects in Python. This is why there is a separate module, copy dedicated to copying Python objects.

The two key functions in the copy module are:

  • copy.copy()
  • copy.deepcopy()

Let’s take a look at what these functions do and what are the differences.

Shallow Copy: copy.copy()

In Python, a shallow copy can be created using copy.copy() function.

A shallow copy solves our problem of copying a list in a way that does not depend on the original list.

For example:

import copy

numbers = [1, 2, 3]

# Independent copy of 'numbers' list
new_numbers = copy.copy(numbers)

numbers[0] = 100

print(numbers)
print(new_numbers)

Output:

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

As you can see, changing the first element in the original list did not change the copied list.

Let’s also verify the objects are not the same by using the id() function:

print(id(numbers))
print(id(new_numbers))

Output:

139764897739904
139764897692480

Horray! Now you know how to create a shallow copy of a list in Python.

But mind the word shallow! It’s important to notice that sometimes you might deal with a list of lists.

In this case, the shallow copy does not behave the way you expect. Instead, it creates an independent copy of the outer list, but the inner lists are bound to the original list.

I know it sounds confusing.

Let me show what this means by running a simple experiment in which I:

  1. Create a list of lists.
  2. Create a shallow copy of the list.
  3. Modify the first list’s first object.
import copy

numbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_numbers = copy.copy(numbers)

numbers[0][0] = 1000

print(numbers)
print(new_numbers)

Output:

[[1000, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1000, 2, 3], [4, 5, 6], [7, 8, 9]]

Here changing the first element of the first list affects the copied version of the list even though the new list should be a copy of the original one.

But why does this happen?

Let’s first compare the IDs of the lists to see if they are the same object:

print(id(numbers))
print(id(new_numbers))

Output:

140602923260928
140602923261632

Even the IDs do not match! This means new_numbers should be a true copy of numbers—and it is!

But why do the values still change in the copied list?

This is because copy.copy() creates a shallow copy.

This means the whole list is copied, but the lists inside the list are not. In other words, the inner lists refer to the lists in the original list.

I know this sounds strange, but this is how it works.

Let’s verify this by checking the IDs of the lists inside the list:

print(id(numbers[0]), id(numbers[1]), id(numbers[2]))
print(id(new_numbers[0]), id(new_numbers[1]), id(new_numbers[2]))

Output:

140685291558208 140685291498496 140685291708160
140685291558208 140685291498496 140685291708160

As you can see, all the inner list IDs are the same.

So the outer list is copied but the inner lists are still bound to the original list of lists.

To put it together, here is an illustration of how copy.copy() works on a list of lists.

Shallow copy nested elements point to the original variable

This highlights the behavior of shallow copying in Python.

As stated earlier, to create a completely independent copy, use the copy.deepcopy() function. Let’s take a closer look at this function to see what it does.

Deep Copy: copy.deepcopy()

Another key function in the copy module is the deepcopy() function.

This function creates a completely independent copy of a list or any other compound object in Python.

For example, let’s repeat the example in the previous chapter using deepcopy():

import copy

numbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_numbers = copy.deepcopy(numbers)

numbers[0][0] = 1000

print(numbers)
print(new_numbers)

Output:

[[1000, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

As you can see, changing the first element in the first list did not affect the copied list.

In other words, you have successfully created a completely independent copy of the original list.

Deep copy in python
No part in the deep-copied list points to the original list. Thus, a deep copy creates a truly independent copy.

I recommend you play with the examples to learn what is happening truly.

Copying a Number Object in Python

This guide would not be complete if we did not talk about copying other objects than lists. It is important to realize everything related to copying lists applies to copying any other Python object.

Let’s repeat the very first example in this guide using integers instead of lists.

In other words, let’s:

  1. Create a number variable.
  2. Copy the number to another variable using the assignment operator.
  3. Change the original number.
  4. See what happens to the copy.
a = 10
b = a

a = 50

print(a, b)

Output:

50 10

As you can see, changing the original number a did not change the number b. This is probably something you would expect.

But this contradicts what we said earlier about copying Python objects: A Python object cannot be copied using the assignment operator.

However, looking at the above example, it seems b is an independent copy of a because changing a does not change b.

Even though this happens, b is not a copy of a. This is important to understand. You can verify this by checking the IDs of the variables before changing the value in a.

a = 10
b = a

print(id(a))
print(id(b))

Output:

9789280
9789280

As you can see, the IDs match. In other words, a and b are both aliases for the same integer object in memory.

But why does changing a not change b then?

It all boils down to mutability.

First of all, you need to recall that a variable is just a label via which you can access an object that lives somewhere in memory. So if you change the value of a variable, you aren’t actually changing the variable itself, but the object in memory that it refers to.

In Python, integer objects are immutable. Immutability means you cannot make direct changes to integer objects. If you assign a new integer to a variable, you create a new integer object in memory and set the existing variable point to that new memory address.

On the other hand, a list is a mutable object. This means you can change the list object directly. So if you assign an existing list to a new variable, you are making the new variable point to the original list.

This describes Python mutability in a nutshell.

Now, let’s go back to the example of copying an integer. Let’s print the IDs of the variables before and after changing the value in a:

a = 10
b = a

print(f"Before assignment id(a) = {id(a)}, id(b) = {id(b)}")

a = 50

print(f"After assignment id(a) = {id(a)}, id(b) = {id(b)}")

Output:

Before assignment id(a) = 9789280, id(b) = 9789280
After assignment id(a) = 9790560, id(b) = 9789280

The IDs of variables a and b match before assigning a new value to a but not afterward.

In other words, before changing the value in a:

  • a and b point to the same integer object in memory.

And after changing the value in a:

  • a points to a new integer object in memory but b still points to where a used to point.

So after assigning a new value to variable a, it points to a new integer object in memory. This happens because of immutability. The integer object 10 cannot directly change. Instead, a new integer object needs to be created.

Here is a quick illustration of how the code works:

Copying an integer with assignment actually creates new object
Assigning a new integer to a creates a new integer object where the variable a points to.

To recap, the assignment operator (=) cannot be used to copy objects in Python. However, when dealing with immutable objects, it looks as if this was the case. But it is not.

If someone tells you to copy a variable, technically you need to use copy.copy() or copy.deepcopy() instead of the assignment operator.

  • However, when dealing with immutable objects, this is unnecessary, as the behavior is the same regardless of whether you used copy module or assignment operator.
  • But with mutable objects, you need to use the copy module to create a real copy of the object.

At this point, you understand why the assignment operator does not copy objects in Python. You also learned how to use the copy module to create copies of Python objects.

Now that you understand what is a shallow copy and a deep copy, let’s put it all together by taking a look at 5 common ways to copy a list in Python.

5 Ways to Copy a List in Python

There are five main ways to copy a list in Python:

  1. copy() method.
  2. [:] slicing operator.
  3. list() function.
  4. copy.copy() function.
  5. copy.deepcopy() function.

Let’s see examples of each of these

1. The copy() Method

As of Python 3.3, a list comes with a built-in copy() method. This method creates a shallow copy of the list.

For example:

numbers = [1, 2, 3]
new_numbers = numbers.copy()

print(numbers)
print(new_numbers)

Output:

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

2. The [:] Slicing operator

In Python, slicing means pulling a range of values from an iterable, such as a list.

Slicing goes with the syntax of:

iterable[start:end]

Where start specifies the starting index and end specifies the ending index.

If you do not specify the start parameter, slicing starts from the very first element. If you do not specify the end, the slicing ends at the very last element.

Calling iterable[:] returns a slice that represents the whole iterable. In other words, it returns a copy of a list when called on a list.

Notice that this also creates a shallow copy.

For instance:

numbers = [1, 2, 3]
new_numbers = numbers[:]

print(numbers)
print(new_numbers)

Output:

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

3. The list() Function

To convert an object to a list in Python, you can use the built-in list() function. This function creates a new list object for the input argument.

When you call the list() function on a list in Python, you force it to create a copy of the original list. The type of this copy is also shallow.

For instance:

numbers = [1, 2, 3]
new_numbers = list(numbers)

print(numbers)
print(new_numbers)

Output:

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

4. The copy.copy() Function

As discussed earlier in this guide, there is a dedicated module copy for copying Python objects.

One of the functions in this module is the copy() function. This function creates a shallow copy of a Python object. You can use copy.copy() to create a copy of a list.

For instance:

import copy

numbers = [1, 2, 3]
new_numbers = copy.copy(numbers)

print(numbers)
print(new_numbers)

Output:

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

5. The copy.deepcopy() Function

The only way to create a truly independent deep copy of a Python object is by using the copy.deepcopy() function.

The difference between a shallow copy and a deep copy is only relevant to objects that consist of objects. This is comprehensively explained earlier in this guide.

You can use copy.deepcopy() to create a deep copy of a list.

For example:

import copy

numbers = [1, 2, 3]
new_numbers = copy.deepcopy(numbers)

print(numbers)
print(new_numbers)

Output:

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

Conclusion

Today you learned how to copy a Python list successfully.

To recap, copying using the assignment operator is not possible. Instead of copying, it creates a new alias to the original object. This means changing the original object changes the “copy” as well.

To truly copy an object in Python, use the copy module’s functions:

  • copy.copy() for a shallow copy where compound objects are bound to the original object.
  • copy.deepcopy() for a deep and completely independent copy.
Scroll to Top