Question

How to Copy a List in Python Without Changing the Original

pythonlistclonemutable

Question

In Python, assigning one list variable to another does not create a separate copy. Why does this happen, and how can you clone or copy a list so changes to the new list do not affect the original?

Example:

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

Output:

[1, 2, 3, 4]

How can you create a true copy of the list instead of another reference to the same list?

Short Answer

By the end of this page, you will understand why new_list = my_list does not copy a list in Python, how list references work, and how to make shallow and deep copies safely depending on the kind of data your list contains.

Concept

In Python, variables do not store objects directly in the way many beginners imagine. Instead, a variable holds a reference to an object.

When you write:

new_list = my_list

both variables point to the same list object in memory. That means there is only one list, and both names refer to it. If you modify that list through either variable, the change is visible through the other.

This matters because lists are mutable, which means they can be changed after creation. Methods like append(), remove(), sort(), and item assignment all modify the existing list.

To avoid unwanted shared changes, you must create a copy.

There are two important kinds of copying in Python:

  • Shallow copy: creates a new outer list, but nested objects inside it are still shared.
  • Deep copy: creates a completely independent copy, including nested lists or other nested mutable objects.

For a simple flat list like [1, 2, 3], a shallow copy is usually enough. For nested structures like [[1, 2], [3, 4]], you may need a deep copy.

This concept appears everywhere in real Python code because accidental shared mutation can cause bugs that are hard to trace.

Mental Model

Think of a Python list like a physical notebook.

  • my_list is a label stuck on the notebook.
  • new_list = my_list does not make a second notebook.
  • It just puts a second label on the same notebook.

So if you write something in the notebook using either label, both labels still point to that same updated notebook.

If you want an independent notebook, you must make a photocopy.

  • A shallow copy is like copying only the main pages, but any attached inserts are still shared.
  • A deep copy is like copying everything, including all inserts and attachments.
Take Quiz

Syntax and Examples

Basic assignment does not copy

my_list = [1, 2, 3]
new_list = my_list

new_list.append(4)

print(my_list)   # [1, 2, 3, 4]
print(new_list)  # [1, 2, 3, 4]

Both variables refer to the same list.

Shallow copy methods

Using slicing

my_list = [1, 2, 3]
new_list = my_list[:]

new_list.append(4)

print(my_list)   # [1, 2, 3]
print(new_list)  # [1, 2, 3, 4]

Using list()

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

Using .copy()

my_list = [1, 2, 3]
new_list = my_list.copy()

All three create a new outer list.

Deep copy for nested lists

import copy

my_list = [[1, 2], [3, 4]]
new_list = copy.deepcopy(my_list)

new_list[0].append(99)

print(my_list)   # [[1, 2], [3, 4]]
print(new_list)  # [[1, 2, 99], [3, 4]]

Why shallow copy can still surprise you

my_list = [[1, 2], [3, 4]]
new_list = my_list.copy()

new_list[0].append(99)

print(my_list)   # [[1, 2, 99], [3, 4]]
print(new_list)  # [[1, 2, 99], [3, 4]]

The outer list was copied, but the inner lists were still shared.

Step by Step Execution

Consider this code:

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

Step by step:

  1. my_list = [1, 2, 3]

    • Python creates one list object containing 1, 2, and 3.
    • The name my_list points to that list.
  2. new_list = my_list

    • No new list is created.
    • new_list now points to the exact same list as my_list.
  3. new_list.append(4)

    • The shared list is modified in place.
    • Since both variables point to the same object, both now see [1, 2, 3, 4].
  4. print(my_list)

    • Output is:
[1, 2, 3, 4]

Now compare that with a real copy:

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

Step by step:

  1. my_list points to [1, 2, 3].
  2. my_list.copy() creates a new list with the same elements.
  3. new_list points to that new separate list.
  4. new_list.append(4) changes only the new list.
  5. Output:
[1, 2, 3]
[1, 2, 3, 4]

Real World Use Cases

Copying lists is common in real programs when you want to work with data safely without mutating the original.

Common situations

  • Processing user input

    • Keep the original values unchanged while transforming a copy.
  • Sorting data for display

    • Show a sorted version of a list without changing the original order.
scores = [88, 72, 95]
sorted_scores = scores.copy()
sorted_scores.sort()
  • Undo or history features

    • Save a snapshot of a list before making changes.
  • API or function safety

    • Prevent a function from accidentally modifying a list passed by the caller.
  • Data pipelines

    • Transform intermediate results without corrupting source data.
  • Testing

    • Create clean copies of test data so one test does not affect another.
Take Quiz

Real Codebase Usage

In real projects, developers often copy lists to avoid side effects and make code easier to reason about.

Common patterns

Defensive copying in functions

def add_item(items, item):
    items = items.copy()
    items.append(item)
    return items

This prevents the caller's original list from being changed.

Copy before sorting or modifying

def get_sorted_names(names):
    result = names.copy()
    result.sort()
    return result

Guarding against nested mutation

If the data contains nested lists or dictionaries, developers often use copy.deepcopy() when full independence is required.

import copy

config_copy = copy.deepcopy(config)

Working with transformations

Sometimes developers avoid mutation entirely and build a new list instead:

numbers = [1, 2, 3]
doubled = [n * 2 for n in numbers]

This is often clearer than copying and then mutating.

Validation and error-safe handling

When parsing or validating incoming data, developers may copy first so the original payload remains available for logging or debugging.

Common Mistakes

1. Thinking assignment creates a copy

Broken example:

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

Fix:

a = [1, 2, 3]
b = a.copy()

2. Using a shallow copy for nested data

Broken example:

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

Fix:

import copy

a = [[1], [2]]
b = copy.deepcopy(a)

3. Confusing rebinding with mutation

This does not modify the original list:

a = [1, 2, 3]
b = a
b = [9, 9, 9]
print(a)  # [1, 2, 3]

Here, b is reassigned to a different object. The original shared list is unchanged.

4. Forgetting that list methods mutate in place

Methods such as these change the list itself:

  • append()
  • extend()
  • insert()
  • remove()
  • sort()
  • reverse()

If you want to preserve the original, copy first.

5. Assuming all copy methods are different for flat lists

For a simple one-dimensional list, these are usually equivalent:

a[:]
list(a)
a.copy()

Comparisons

ApproachCreates new outer list?Copies nested objects too?Best for
b = aNoNoJust another reference
a[:]YesNoSimple shallow copy
list(a)YesNoCopying any iterable into a list
a.copy()YesNoClear shallow copy of a list
copy.deepcopy(a)YesYesNested mutable data

a.copy() vs slicing

  • a.copy() is very readable and clearly says "make a copy".
  • a[:] is short and common, but less obvious to beginners.

Shallow copy vs deep copy

TypeWhat it copiesWhen to use
Shallow copyOnly the outer containerFlat lists or when nested items can be shared
Deep copyOuter container and nested mutable objectsNested lists, dicts, or complex structures that must be independent
Take Quiz

Cheat Sheet

Quick reference

Assignment

b = a
  • No copy is made
  • a and b point to the same list

Shallow copy

b = a.copy()
b = a[:]
b = list(a)
  • New outer list
  • Nested mutable items are still shared

Deep copy

import copy
b = copy.deepcopy(a)
  • Fully independent copy
  • Use for nested lists or dictionaries

Rule of thumb

  • Use a.copy() for normal flat lists
  • Use copy.deepcopy(a) for nested mutable data
  • Do not use b = a if you need independence

Common mutating list methods

append()
extend()
insert()
remove()
pop()
sort()
reverse()

These change the existing list.

Safe pattern

new_list = old_list.copy()

Edge case

a = [[1, 2]]
b = a.copy()
b[0].append(3)

a also changes because the inner list is shared.

FAQ

Why does new_list = my_list change both lists in Python?

Because it does not create a new list. It makes both variables refer to the same list object.

What is the best way to copy a list in Python?

For most flat lists, my_list.copy() is the clearest choice.

What is the difference between shallow copy and deep copy?

A shallow copy duplicates only the outer list. A deep copy also duplicates nested mutable objects.

Is slicing like my_list[:] a real copy?

Yes, it creates a shallow copy of the list.

When do I need copy.deepcopy()?

Use it when your list contains nested lists, dictionaries, or other mutable objects that must not be shared.

Does list(my_list) copy the list?

Yes, it creates a shallow copy when my_list is already a list.

Which list operations modify the original list?

Methods like append(), extend(), remove(), sort(), and reverse() modify the list in place.

Take Quiz

Mini Project

Description

Build a small Python script that manages a shopping list and creates a safe backup before making changes. This demonstrates the difference between simple assignment and actual copying, and shows why copying matters when you want to preserve original data.

Goal

Create a program that keeps an original shopping list unchanged while modifying a copied version.

Requirements

  1. Create an original shopping list with at least three items.
  2. Make one variable using direct assignment and another using a real copy.
  3. Modify both new variables in different ways.
  4. Print all lists so the difference is visible.
  5. Add one nested-list example to demonstrate when deepcopy() is needed.
Take Quiz

Keep learning

Related questions

View more