Want to copy a list in Python? Call new = old.copy(), or use the slice old[:]. Both give you a separate list, so changing one won’t touch the other. For lists that contain other lists, use copy.deepcopy():
new = old.copy()
new = old[:]
import copy
new = copy.deepcopy(nested)
Screenshots come straight from Python 3.12.5 in Command Prompt, with the output text pasted under each code block.
I’ll start with the mistake that makes copying necessary, then show five ways to copy, the shallow vs deep difference, which method is fastest, and copying inside functions.
Why = doesn’t copy a list in Python
Assignment never copies anything. same = cart just puts a second name on the same list, so a change through either name shows up in both:
cart = ["coffee", "bagel", "juice"]
same = cart # NOT a copy, just a second name
same.append("muffin")
print("cart:", cart) # changed too!
print("same object?", same is cart)
Output:
cart: ['coffee', 'bagel', 'juice', 'muffin']
same object? True

same changed cart, because same is cart is True.This catches almost everyone at first. Mutable vs immutable in Python explains why lists behave this way and strings don’t.
5 ways to copy a list in Python
Each of these gives you a new list with the same items. They’re interchangeable for a flat list of numbers or strings:
import copy
cart = ["coffee", "bagel", "juice"]
a = cart.copy()
b = cart[:]
c = list(cart)
d = copy.copy(cart)
e = [*cart]
a.append("muffin")
print("original:", cart)
print("a: ", a)
print("all equal and separate:", b == c == d == e == cart, b is not cart)
Output:
original: ['coffee', 'bagel', 'juice']
a: ['coffee', 'bagel', 'juice', 'muffin']
all equal and separate: True True

a left the original alone. All five copies are equal to it but separate objects.| Method | Code | Notes |
|---|---|---|
| list.copy() | new = old.copy() | Clearest; what I use |
| Slice | new = old[:] | Classic, very common in older code |
| list() | new = list(old) | Also converts tuples, sets, etc. |
| copy.copy() | new = copy.copy(old) | Works on any object |
| Unpacking | new = [*old] | Handy when adding items too |
Shallow copy vs deep copy of a list
Every method above makes a shallow copy. The outer list is new, but the items inside are shared. With nested lists, that means the inner lists are shared:
import copy
orders = [["Austin", 2], ["Denver", 5]]
shallow = orders.copy()
deep = copy.deepcopy(orders)
orders[0][1] = 99 # change something inside an inner list
orders.append(["Boston", 1]) # and add a new row
print("original:", orders)
print("shallow: ", shallow) # inner list is shared, new row isn't
print("deep: ", deep) # fully independent
print(shallow[0] is orders[0], deep[0] is orders[0])
Output:
original: [['Austin', 99], ['Denver', 5], ['Boston', 1]]
shallow: [['Austin', 99], ['Denver', 5]]
deep: [['Austin', 2], ['Denver', 5]]
True False

Appending a new row didn’t affect either copy, since that only changes the outer list. For the full picture, see shallow copy vs deep copy in Python and list of lists in Python.
Which way to copy a list is fastest?
For a flat list, the built-in methods are all quick. deepcopy() is far slower because it inspects every item, so only use it when you need it:
import copy
import timeit
data = list(range(1_000))
tests = {
"list.copy()": lambda: data.copy(),
"data[:]": lambda: data[:],
"list(data)": lambda: list(data),
"copy.copy()": lambda: copy.copy(data),
"copy.deepcopy()": lambda: copy.deepcopy(data),
"comprehension": lambda: [x for x in data],
}
for name, fn in tests.items():
t = timeit.timeit(fn, number=2_000)
print(f"{name:<16} {t * 1000:8.2f} ms")
Output:
list.copy() 2.74 ms
data[:] 2.81 ms
list(data) 3.33 ms
copy.copy() 2.86 ms
copy.deepcopy() 322.55 ms
comprehension 23.68 ms

copy(), slicing and list() are neck and neck. Your exact numbers will vary.Copying a list inside a function
Python passes the list itself into your function, not a copy. If your function changes it, the caller sees the change. Copy first when you want to leave the original alone:
def add_tax(prices, rate=0.08):
prices = prices.copy() # work on a copy so the caller's list is safe
for i, p in enumerate(prices):
prices[i] = round(p * (1 + rate), 2)
return prices
menu = [4.50, 2.75, 3.25]
print("with tax:", [f"${p:.2f}" for p in add_tax(menu)])
print("menu: ", [f"${p:.2f}" for p in menu]) # untouched
Output:
with tax: ['$4.86', '$2.97', '$3.51']
menu: ['$4.50', '$2.75', '$3.25']
Returning a new list like this makes functions easier to test and reason about. The loop uses enumerate() to update each price by position.
For more list skills, try these next:
- Copy elements from one list to another
- Append a list to another list
- Reverse a list in Python
- Python list comprehension
Frequently asked questions
How do I copy a list in Python?
Use new = old.copy() or new = old[:]. For nested lists, use copy.deepcopy(old).
Why does changing my copy change the original list?
You probably wrote new = old, which doesn’t copy. Or the list is nested and you made a shallow copy.
What’s the difference between copy() and deepcopy()?
copy() copies the outer list only. deepcopy() copies everything inside it too, recursively.
Is list[:] the same as list.copy()?
Yes, for lists they give the same shallow copy. copy() just reads more clearly.
Does deepcopy work on lists of dictionaries?
Yes. It copies the dictionaries and anything inside them, so nothing is shared with the original.
Where is copying documented?
The copy module docs explain shallow and deep copies.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn