python list by value not by reference
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
In Python, assigning a list to a new variable does not create a copy — it creates a second reference to the same list object. Modifying the list through either variable affects both. To get an independent copy, you need to explicitly create one using slicing (list[:]), list.copy(), list(), or copy.deepcopy() for nested structures. Understanding this behavior is essential for avoiding subtle bugs where one function unintentionally modifies another's data.
The Reference Problem
alias = original does not copy the list. Both names refer to the same object. Any mutation through one variable is visible through the other.
Shallow Copy Methods
All four methods create a new list object with the same elements. Modifying the copy does not affect the original. These are called "shallow copies" because they copy the list structure but not the objects inside it.
The Shallow Copy Trap
A shallow copy creates a new outer list, but the elements inside still reference the same objects. For immutable elements (ints, strings, tuples), this is fine. For mutable elements (lists, dicts), changes propagate between the copy and the original.
Deep Copy for Nested Structures
copy.deepcopy() recursively copies every nested object, creating a fully independent clone. Use it when your list contains mutable elements that should not be shared.
Function Arguments
Python passes objects by reference. When a function receives a list, it gets a reference to the same list. Mutations inside the function affect the caller's data. Copy the list if the function should not modify the original.
Default Mutable Arguments
Default mutable arguments are created once when the function is defined, not each time it is called. This is one of the most common Python gotchas related to reference semantics.
List Comprehension Creates a New List
List comprehensions and generator expressions that materialize into lists (list(gen)) always produce new list objects. They are a natural way to create independent copies with optional transformation.
When to Use Each Copy Method
| Scenario | Method | Copy Depth |
| Flat list of immutables | list.copy() or [:] | Shallow (sufficient) |
| Flat list of mutables | copy.deepcopy() | Deep |
| Nested lists/dicts | copy.deepcopy() | Deep |
| Filtering or transforming | List comprehension | New list |
| Function argument protection | list.copy() at call site | Shallow |
Common Pitfalls
- Assuming
=copies a list:new = oldcreates an alias, not a copy. Any change tonewchangesold. Always use.copy(),[:], orlist()to copy. - Using shallow copy for nested lists:
.copy()only copies the top-level list. Nested mutable objects are still shared. Usecopy.deepcopy()for nested structures. - Mutable default arguments:
def f(items=[])shares the same list across all calls. UseNoneas the default and create a new list inside the function. - Forgetting that slicing creates copies but indexing does not:
original[:]creates a copy, butoriginal[0]returns a reference to the element. For mutable elements likeoriginal[0]being a list, modifying it changes the original. - Overusing deepcopy:
copy.deepcopy()is slow because it recursively copies every object and handles circular references. For flat lists of simple types,.copy()is sufficient and much faster.
Summary
new = oldcreates a reference (alias), not a copy — both variables point to the same list- Use
.copy(),[:],list(), orcopy.copy()for shallow copies of flat lists - Use
copy.deepcopy()when the list contains nested mutable objects (lists, dicts) - Functions receive list references — copy before mutating if the original should be preserved
- Avoid mutable default arguments (
def f(items=[])) — useNoneand create a new list inside - List comprehensions always produce new independent lists
Related reading
- Python List of dict, if exists increment a dict value, if not append a new dict
- Python list sort in descending order
- Python list vs. array – when to use?
- Python List vs Dict for look up table
- Python list directory, subdirectory, and files
- Python locale error unsupported locale setting
- Python memory usage of numpy arrays
- python numpy ValueError operands could not be broadcast together with shapes

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.