Python Copy Lists

When you work with lists in Python, you will often need to create a separate copy of a list so you can modify it without changing the original. Python copy lists using several built-in techniques, each suited for different situations. Understanding how to properly copy lists in Python is essential because simply assigning a list to a new variable does not create an independent copy. Instead, it creates a reference to the same list in memory, which means changes to one variable affect the other.

This is one of the most common mistakes beginners make when working with lists. Once you understand why direct assignment does not copy a list and learn the correct methods to copy lists in Python, you will avoid subtle bugs that are difficult to track down. Python gives you multiple ways to create copies, from quick one-liners to methods that handle complex nested structures.

Why Assignment Does Not Copy a List

Before learning how to copy lists in Python, you need to understand what happens when you assign a list to a new variable. Python does not create a new list. Instead, both variables point to the exact same list object in memory.

original = [1, 2, 3, 4, 5]
assigned = original
assigned.append(6)
print("Original:", original)
print("Assigned:", assigned)
print("Same object:", original is assigned)
Output
Original: [1, 2, 3, 4, 5, 6]
Assigned: [1, 2, 3, 4, 5, 6]
Same object: True

Both variables show the appended value because they reference the same list object. The is operator confirms they point to the same memory location. This is why you need to explicitly copy lists in Python when you want two independent lists to work with.

Copy a List Using the copy Method

The copy method is the most straightforward way to copy lists in Python. It creates a new list with the same elements as the original, giving you an independent copy that you can modify without affecting the source list.

colors = ["red", "green", "blue", "yellow"]
colors_copy = colors.copy()
colors_copy.append("purple")
print("Original:", colors)
print("Copy:", colors_copy)
print("Same object:", colors is colors_copy)
Output
Original: ['red', 'green', 'blue', 'yellow']
Copy: ['red', 'green', 'blue', 'yellow', 'purple']
Same object: False

The copy method created a brand new list with the same elements. Adding an item to the copy did not change the original list. The is operator now returns False, confirming these are two separate objects in memory. This method was introduced in Python 3.3 and is the recommended way to copy lists in Python for simple, flat lists.

Copy a List Using Slicing

Slicing is another popular technique to copy lists in Python. By using the slice notation with no start or end index, you create a new list that contains all elements from the original.

numbers = [10, 20, 30, 40, 50]
numbers_copy = numbers[:]
numbers_copy[0] = 99
print("Original:", numbers)
print("Copy:", numbers_copy)
Output
Original: [10, 20, 30, 40, 50]
Copy: [99, 20, 30, 40, 50]

The slice notation creates a completely new list. Changing the first element in the copy did not affect the original list at all. Many experienced Python developers use this slicing approach to copy lists in Python because it is concise and works in both Python 2 and Python 3. The empty slice is equivalent to writing the full slice notation with default values for start, stop, and step.

Copy a List Using the list Constructor

The list constructor provides yet another way to copy lists in Python. You pass the original list as an argument to the list function, and it creates a new list containing all the same elements.

fruits = ["apple", "banana", "cherry", "mango"]
fruits_copy = list(fruits)
fruits_copy.remove("banana")
print("Original:", fruits)
print("Copy:", fruits_copy)
Output
Original: ['apple', 'banana', 'cherry', 'mango']
Copy: ['apple', 'cherry', 'mango']

The list constructor iterated over the original list and built a fresh list from those elements. Removing an item from the copy left the original untouched. This approach to copy lists in Python is explicit and readable, making your intention clear to anyone reading your code. It works with any iterable, not just lists, so it doubles as a conversion tool when needed.

Shallow Copy vs Deep Copy

All three methods shown so far create what is called a shallow copy. A shallow copy creates a new list object but does not create copies of the objects inside the list. For simple data types like numbers and strings, this works perfectly. But when your list contains mutable objects like other lists or dictionaries, a shallow copy can lead to unexpected behavior.

matrix = [[1, 2], [3, 4], [5, 6]]
shallow = matrix.copy()
shallow[0][0] = 99
print("Original:", matrix)
print("Shallow copy:", shallow)
Output
Original: [[99, 2], [3, 4], [5, 6]]
Shallow copy: [[99, 2], [3, 4], [5, 6]]

Even though we used the copy method to copy this list in Python, modifying a nested list inside the copy also changed the original. This happened because the shallow copy only created a new outer list. The inner lists are still shared references between the original and the copy. Both lists point to the same inner list objects in memory.

Copy Nested Lists Using deepcopy

When you need to copy lists in Python that contain nested mutable objects, you need the deepcopy function from the copy module. A deep copy recursively creates new copies of every object inside the list, giving you a completely independent duplicate.

import copy

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
deep = copy.deepcopy(matrix)
deep[0][0] = 99
deep[1].append(10)
print("Original:", matrix)
print("Deep copy:", deep)
Output
Original: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Deep copy: [[99, 2, 3], [4, 5, 6, 10], [7, 8, 9]]

The deepcopy function created independent copies of every nested list. Modifying the inner lists in the deep copy had no effect on the original. This is the only reliable way to copy lists in Python when dealing with nested or complex data structures. The copy module handles circular references and other edge cases automatically, making deepcopy safe to use with any data structure.

Copy a List Using a List Comprehension

A list comprehension can also be used to copy lists in Python. This approach creates a new list by iterating over each element in the original.

scores = [85, 92, 78, 95, 88]
scores_copy = [item for item in scores]
scores_copy.sort(reverse=True)
print("Original:", scores)
print("Copy sorted:", scores_copy)
Output
Original: [85, 92, 78, 95, 88]
Copy sorted: [95, 92, 88, 85, 78]

The list comprehension built a new list element by element. Sorting the copy in descending order did not affect the original list. While this is a valid way to copy lists in Python, it is more commonly used when you want to transform elements during the copy. For a plain copy without transformation, the copy method or slicing is more readable and conventional.

Copy a List with Mixed Data Types

Python lists can hold different data types in the same list, and all copy methods handle mixed-type lists the same way. You can copy lists in Python regardless of what types of elements they contain.

mixed = [42, "hello", 3.14, True, None]
mixed_copy = mixed.copy()
mixed_copy[1] = "world"
mixed_copy.append([1, 2])
print("Original:", mixed)
print("Copy:", mixed_copy)
Output
Original: [42, 'hello', 3.14, True, None]
Copy: [42, 'world', 3.14, True, None, [1, 2]]

The copy preserved all elements regardless of their types. Modifying the string element and appending a new list to the copy did not impact the original. Since strings, numbers, booleans, and None are all immutable, a shallow copy is perfectly safe for lists containing only these types.

Copy a List of Dictionaries

Lists containing dictionaries are a common data structure in Python. When you copy lists in Python that hold dictionaries, you need to be aware of how shallow and deep copies behave differently.

import copy

students = [
    {"name": "Alice", "grade": 90},
    {"name": "Bob", "grade": 85}
]

shallow = students.copy()
deep = copy.deepcopy(students)

shallow[0]["grade"] = 95
deep[1]["grade"] = 100

print("Original:", students)
print("Shallow:", shallow)
print("Deep:", deep)
Output
Original: [{'name': 'Alice', 'grade': 95}, {'name': 'Bob', 'grade': 85}]
Shallow: [{'name': 'Alice', 'grade': 95}, {'name': 'Bob', 'grade': 85}]
Deep: [{'name': 'Alice', 'grade': 90}, {'name': 'Bob', 'grade': 100}]

The shallow copy shared the same dictionary objects with the original, so changing a grade in the shallow copy also changed the original. The deep copy created independent dictionary objects, so its modifications stayed isolated. When you copy lists in Python that contain dictionaries, always use deepcopy if you plan to modify the dictionary contents.

Full Working Example

This example demonstrates all the major techniques to copy lists in Python, showing how each method behaves with both flat and nested data.

import copy

print("=== Direct Assignment (Not a Copy) ===")
original = [1, 2, 3]
assigned = original
assigned.append(4)
print("Original:", original)
print("Assigned:", assigned)
print("Same object:", original is assigned)

print("\n=== copy() Method ===")
letters = ["a", "b", "c", "d"]
letters_copy = letters.copy()
letters_copy.append("e")
print("Original:", letters)
print("Copy:", letters_copy)

print("\n=== Slicing ===")
values = [10, 20, 30, 40]
values_copy = values[:]
values_copy[0] = 99
print("Original:", values)
print("Copy:", values_copy)

print("\n=== list() Constructor ===")
words = ["hello", "world", "python"]
words_copy = list(words)
words_copy.remove("world")
print("Original:", words)
print("Copy:", words_copy)

print("\n=== List Comprehension ===")
nums = [5, 10, 15, 20]
nums_copy = [x for x in nums]
nums_copy.reverse()
print("Original:", nums)
print("Copy:", nums_copy)

print("\n=== Shallow Copy with Nested List ===")
nested = [[1, 2], [3, 4]]
shallow = nested.copy()
shallow[0][0] = 99
print("Original:", nested)
print("Shallow:", shallow)

print("\n=== Deep Copy with Nested List ===")
nested2 = [[10, 20], [30, 40]]
deep = copy.deepcopy(nested2)
deep[0][0] = 99
print("Original:", nested2)
print("Deep:", deep)

print("\n=== Deep Copy with Dictionaries ===")
records = [
    {"id": 1, "scores": [80, 90]},
    {"id": 2, "scores": [70, 85]}
]
records_copy = copy.deepcopy(records)
records_copy[0]["scores"].append(95)
records_copy[1]["id"] = 99
print("Original:", records)
print("Deep copy:", records_copy)
Output
=== Direct Assignment (Not a Copy) ===
Original: [1, 2, 3, 4]
Assigned: [1, 2, 3, 4]
Same object: True

=== copy() Method ===
Original: ['a', 'b', 'c', 'd']
Copy: ['a', 'b', 'c', 'd', 'e']

=== Slicing ===
Original: [10, 20, 30, 40]
Copy: [99, 20, 30, 40]

=== list() Constructor ===
Original: ['hello', 'world', 'python']
Copy: ['hello', 'python']

=== List Comprehension ===
Original: [5, 10, 15, 20]
Copy: [20, 15, 10, 5]

=== Shallow Copy with Nested List ===
Original: [[99, 2], [3, 4]]
Shallow: [[99, 2], [3, 4]]

=== Deep Copy with Nested List ===
Original: [[10, 20], [30, 40]]
Deep: [[99, 20], [30, 40]]

=== Deep Copy with Dictionaries ===
Original: [{'id': 1, 'scores': [80, 90]}, {'id': 2, 'scores': [70, 85]}]
Deep copy: [{'id': 1, 'scores': [80, 90, 95]}, {'id': 99, 'scores': [70, 85]}]