E0_copy is not a deep copy. You don't make a deep copy using list(). (Both list(...) and testList[:] are shallow copies, as well as testList.copy().)

You use copy.deepcopy(...) for deep copying a list.

copy.deepcopy(x[, memo])

Return a deep copy of x.

See the following snippet -

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b   # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]

Now see the deepcopy operation

>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b    # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]

To explain, list(...) does not recursively make copies of the inner objects. It only makes a copy of the outermost list, while still referencing the same inner lists, hence, when you mutate the inner lists, the change is reflected in both the original list and the shallow copy. You can see that shallow copying references the inner lists by checking that id(a[0]) == id(b[0]) where b = list(a).

Answer from Sukrit Kalra on Stack Overflow
๐ŸŒ
Python
docs.python.org โ€บ 3 โ€บ library โ€บ copy.html
copy โ€” Shallow and deep copy operations
February 23, 2026 - A shallow copy constructs a new ... found in the original. A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original....
Top answer
1 of 10
416

E0_copy is not a deep copy. You don't make a deep copy using list(). (Both list(...) and testList[:] are shallow copies, as well as testList.copy().)

You use copy.deepcopy(...) for deep copying a list.

copy.deepcopy(x[, memo])

Return a deep copy of x.

See the following snippet -

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b   # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]

Now see the deepcopy operation

>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b    # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]

To explain, list(...) does not recursively make copies of the inner objects. It only makes a copy of the outermost list, while still referencing the same inner lists, hence, when you mutate the inner lists, the change is reflected in both the original list and the shallow copy. You can see that shallow copying references the inner lists by checking that id(a[0]) == id(b[0]) where b = list(a).

2 of 10
104

In Python, there is a module called copy with two useful functions:

import copy
copy.copy()
copy.deepcopy()

copy() is a shallow copy function. If the given argument is a compound data structure, for instance a list, then Python will create another object of the same type (in this case, a new list) but for everything inside the old list, only their reference is copied. Think of it like:

newList = [elem for elem in oldlist]

Intuitively, we could assume that deepcopy() would follow the same paradigm, and the only difference is that for each elem we will recursively call deepcopy, (just like mbguy's answer)

but this is wrong!

deepcopy() actually preserves the graphical structure of the original compound data:

a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)

# check the result
c[0] is a # False, a new object a_1 is created
c[0] is c[1] # True, c is [a_1, a_1] not [a_1, a_2]

This is the tricky part: during the process of deepcopy(), a hashtable (dictionary in Python) is used to map each old object ref onto each new object ref, which prevents unnecessary duplicates and thus preserves the structure of the copied compound data.

Official docs

Discussions

Do I need to be using deepcopy with numpy arrays in this situation?
The short answer is that if you have, say, a numpy array of ints, floats, etc (which is usually) and you want to make a separate copy where the two will not interact in any way (which is what it sounds like), there's no need to deepcopy, and you can just use the array.copy() method. Long version: You are also correct on copy vs deepcopy. If you use copy on, say, a list (more on arrays later), you will get a different list containing the same elements of the original. So: l = [[1], [2]] l2 = copy.copy(l) # l2 is different from l # The sublists inside l2 are the same as the ones in l l2[0] = 42 # l unchanged, you modified l2 which is different l2[1][0] = 1337 # reflected in l, because l2[1] is the same as l[1] So in this case, if you didn't want l2[1][0] = 1337 to be reflected in l, you'd use deepcopy. This would ensure that the sublists are different, as well as the outer list. However, if the contents of l are say, floats or ints, then this doesn't matter. Floats are immutable - you can't change em. What you might think of as modifying a float is actually creating a new float and putting that new float somewhere. So: l = [1.2, 3.4] l2 = l.copy() # same as copy.copy(l) # The elements of l2 are still the same as of l as before. But: l2[0] += 7 # does not actually modify the # float 1.2, but creates a new # float 8.2 and assigns that to # l2[0] - which modifies l2, which # is different from l, so l is unchanged So in this case deepcopy is not necessary to make l and l2 behave completely distinctly (though it doesn't hurt anything - might use a bit more ram depending on what types are in l). Numpy arrays are almost certainly a bit different though. For one thing, a single numpy array with shape (5, 5), say, is not actually an array of arrays, but a single sequence of length 25 that uses math to let you use double indexing. Also, under the hood numpy arrays have byte buffers representing whatever types, so I strongly suspect that array.copy just copies that buffer (at least if it's an array of numbers). So it's possible that it's not really the same rules as lists of objects anyway. But .copy is certainly sufficient for what you want. More on reddit.com
๐ŸŒ r/learnpython
1
1
April 21, 2023
python - Deep copy of a np.array of np.array - Stack Overflow
I have a numpy array of different numpy arrays and I want to make a deep copy of the arrays. I found out the following: import numpy as np pairs = [(2, 3), (3, 4), (4, 5)] array_of_arrays = np.ar... More on stackoverflow.com
๐ŸŒ stackoverflow.com
Python copy.copy() appears to make a deep copy
The difference between a deep copy and a shallow copy is whether or not nested mutable structures are (recursively) copied (deep) or just referenced (shallow). All elements of your list are immutable. Therefore, there is no difference between a shallow and a deep copy. A proper demonstration of the difference is this: import copy my_list = [['first'], ['second'], ['third']] my_deep_copy = copy.deepcopy(my_list) my_deep_copy[1][0] = 'new value' print(my_list) my_shallow_copy = copy.copy(my_list) my_shallow_copy[1][0] = 'new value' print(my_list) Note how it requires nested mutable structures. More on reddit.com
๐ŸŒ r/learnpython
10
October 17, 2022
Why choose to copy a list with slices instead of copy(), deepcopy(), or list()??
Does Python just have multiple ways to copy a list in the same way??? Although most of the approaches mentioned can be used to copy lists, that doesn't mean that's what they're for! Every operation has a different purpose: Using the slicing technique The purpose of slicing is to select an arbitrary subset of a list. Slices can be used to either obtain a copy of this subset, or to modify the selected subset. For example: foo = [1, 2, 3, 4, 5] print(foo[1:3]) # will print: [2, 3] foo[1:3] = ['a', 'b', 'c', 'd'] print(foo) # will print: [1, a, b, c, d, 4, 5] 2. Using the extend() method The purpose of extend is to add a whole other collection of items onto the end of an existing list. Although slicing can be used for this too, .extend() is less error prone since you don't have to worry about calculating the correct indexes. 3. List copy using =(assignment operator) Geeksforgeeks is wrong here. The = operator doesn't make a copy. Rather, you simply have two different variables refer to the exact same list. 4. Using the method of Shallow Copy Python's copy module wasn't made for lists. The copy() and deepcopy() functions are able to make copies of any arbitrary Python object. For example: from copy import copy class Person: def __init__(self, fname, lname): self.first_name = fname self.last_name = lname def print_greeting(self): print(f"Hello! My name is {self.first_name} {self.last_name}.") foo = Person("Jack", "Johnson") foo.print_greeting() # Will print: Hello! My name is Jack Johnson. bar = copy(foo) bar.print_greeting() # Will print: Hello! My name is Jack Johnson. foo.first_name = "John" foo.last_name = "Jackson" foo.print_greeting() # Will print: Hello! My name is John Jackson bar.print_greeting() # Will print: Hello! My name is Jack Johnson As you see, the copy operation actually made a copy of our Person object. The fact that it works for lists is just a consequence, but that doesn't make it the way of copying lists. 5. Using list comprehension List comprehensions are for constructing a new list based on the data in existing collections. Basically, they're meant to be a concise way to compose map() and filter() operations. You can also use it to quickly construct the cartesian product of multiple collections. 6. Using the append() method The append operation is simply for adding a single item onto a list. Nothing more. Manually writing your own for-loop to append items one by one is perhaps the most clucky & verbose way to copy a list. 7. Using the copy() method This is the only operation whose specific purpose is to copy a Python list. It's finely tuned just for that. Thus, it'll offer you the best performance. 8. Using the method of Deep Copy See #4. The deepcopy() function can make a copy of any arbitrary object, not just a list. 9. The list() function (not mentioned on geeksforgeeks). This is simply the list class constructor. Since 'list' is a class, it has to have a constructor, so might as well make it useful. It can accept any arbitrary collection - a set, a dictionary, a generator, a custom collection class, basically any object that has __iter__ defined on it. It uses the iterator interface to copy each item one by one into the new Python list. More on reddit.com
๐ŸŒ r/AskProgramming
11
1
November 3, 2022
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ array-copying-in-python
Array Copying in Python - GeeksforGeeks
April 30, 2025 - Explanation: The deepcopy() method creates a completely independent copy of the 2D array (image). The code rotates the matrix by first reversing each row and then transposing the result.
๐ŸŒ
NumPy
numpy.org โ€บ doc โ€บ 2.1 โ€บ reference โ€บ generated โ€บ numpy.copy.html
numpy.copy โ€” NumPy v2.1 Manual
Note that, np.copy clears previously set WRITEABLE=False flag. >>> a = np.array([1, 2, 3]) >>> a.flags["WRITEABLE"] = False >>> b = np.copy(a) >>> b.flags["WRITEABLE"] True >>> b[0] = 3 >>> b array([3, 2, 3])
๐ŸŒ
Tutorialspoint
tutorialspoint.com โ€บ python โ€บ python_copy_arrays.htm
Python - Copy Arrays
To create another physical copy of an array, we use another module in Python library, named copy and use deepcopy() function in the module.
๐ŸŒ
NumPy
numpy.org โ€บ doc โ€บ stable โ€บ reference โ€บ generated โ€บ numpy.copy.html
numpy.copy โ€” NumPy v2.4 Manual
Note that, np.copy clears previously set WRITEABLE=False flag. >>> a = np.array([1, 2, 3]) >>> a.flags["WRITEABLE"] = False >>> b = np.copy(a) >>> b.flags["WRITEABLE"] True >>> b[0] = 3 >>> b array([3, 2, 3])
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ do i need to be using deepcopy with numpy arrays in this situation?
r/learnpython on Reddit: Do I need to be using deepcopy with numpy arrays in this situation?
April 21, 2023 -

I've done a ton of reading about this and I sort of know the difference between the two, but I still don't know when I need to use copy.copy() vs copy.deepcopy(). It seems like copy.copy() makes a new object of the "container" (for instance, if it is a list then you get a new list object) but then populates this container with the references to the original element objects (so each element in the two different lists point to the same spot in memory).

Essentially, my question is that I have a bunch of numpy arrays and I don't think I'm using copy correctly (or if I need ot be using it at all). It seems like if I am only doing assignments or using these values to set other values (eg output = self.weights*input or whatever) then I don't need to make copies (its fine that the references point to the same spot in memory since I just need read not write access), but if I am doing things like increments / decrements or setting the new value based on the previous value, then I would be changing the values in memory and these are shared by all my objects? Some code:

For instance, let's say I have some Parent class and it has its children Child objects and the parent sends its models to the children and the children update the models based on their own data. Specifically, each child should get (a copy?) the parent's weight matrix (numpy array) to use as initialization, and then start updating its own matrix (not shared with any other children or the parent).

parent = Parent()
child1 = Child()
child2 = Child()
...
childN = Child()

for child in children_list:
    child.weights = parent.weights  # copy? deepcopy?

for child in children_list:
    for _ in num_grad_steps:
        child.weights -= child.learning_rate * child.gradient(child.weights, child.input_data)
    
    # Send the updated weights back to the parent
    parent.new_weights_list.append(child.weights)

In this case, child.weights is being updated by each child (note I don't want a single matrix that is updated by all children, I want one matrix as initialization, then every child runs with it so I end up with N unique final matrices), and if it is either just the same object (basic assignment: child.weights = self.weights) or just a shallow copy (child.weights = copy.copy(parent.weights)) then FOR ALL THE CHILDREN the weights are shared instead of each child getting the same initial matrix and privately updating its own copy, right? If I instead switch this to child.weights = copy.deepcopy(parent.weights) then I think this fixes it, but since my weights matrix is pretty large this just takes an extremely long time to run (and it seems like I'm missing something, like it shouldn't be this hard. I feel like code I see online in similar applications doesn't do this, but many don't have the weights distributed across so many objects at once I guess?). Do I need to be using deepcopy or is there something I'm missing? Greatly appreciate any help!

Top answer
1 of 1
2
The short answer is that if you have, say, a numpy array of ints, floats, etc (which is usually) and you want to make a separate copy where the two will not interact in any way (which is what it sounds like), there's no need to deepcopy, and you can just use the array.copy() method. Long version: You are also correct on copy vs deepcopy. If you use copy on, say, a list (more on arrays later), you will get a different list containing the same elements of the original. So: l = [[1], [2]] l2 = copy.copy(l) # l2 is different from l # The sublists inside l2 are the same as the ones in l l2[0] = 42 # l unchanged, you modified l2 which is different l2[1][0] = 1337 # reflected in l, because l2[1] is the same as l[1] So in this case, if you didn't want l2[1][0] = 1337 to be reflected in l, you'd use deepcopy. This would ensure that the sublists are different, as well as the outer list. However, if the contents of l are say, floats or ints, then this doesn't matter. Floats are immutable - you can't change em. What you might think of as modifying a float is actually creating a new float and putting that new float somewhere. So: l = [1.2, 3.4] l2 = l.copy() # same as copy.copy(l) # The elements of l2 are still the same as of l as before. But: l2[0] += 7 # does not actually modify the # float 1.2, but creates a new # float 8.2 and assigns that to # l2[0] - which modifies l2, which # is different from l, so l is unchanged So in this case deepcopy is not necessary to make l and l2 behave completely distinctly (though it doesn't hurt anything - might use a bit more ram depending on what types are in l). Numpy arrays are almost certainly a bit different though. For one thing, a single numpy array with shape (5, 5), say, is not actually an array of arrays, but a single sequence of length 25 that uses math to let you use double indexing. Also, under the hood numpy arrays have byte buffers representing whatever types, so I strongly suspect that array.copy just copies that buffer (at least if it's an array of numbers). So it's possible that it's not really the same rules as lists of objects anyway. But .copy is certainly sufficient for what you want.
Find elsewhere
Top answer
1 of 7
34
import numpy as np
import copy

pairs = [(2, 3), (3, 4), (4, 5)]
array_of_arrays = np.array([np.arange(a*b).reshape(a,b) for (a, b) in pairs])

a = copy.deepcopy(array_of_arrays)

Feel free to read up more about this here.

Oh, here is simplest test case:

a[0][0,0]
print a[0][0,0], array_of_arrays[0][0,0]
2 of 7
5
In [276]: array_of_arrays
Out[276]: 
array([array([[0, 1, 2],
       [3, 4, 5]]),
       array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]]),
       array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])], dtype=object)

array_of_arrays is dtype=object; that means each element of the array is a pointer to an object else where in memory. In this case those elements are arrays of different sizes.

a = array_of_arrays[:]

a is a new array, but a view of array_of_arrays; that is, it has the same data buffer (which in this case is list of pointers).

b = array_of_arrays[:][:] 

this is just a view of a view. The second [:] acts on the result of the first.

c = np.array(array_of_arrays, copy=True)

This is the same as array_of_arrays.copy(). c has a new data buffer, a copy of the originals

If I replace an element of c, it will not affect array_of_arrays:

c[0] = np.arange(3)

But if I modify an element of c, it will modify the same element in array_of_arrays - because they both point to the same array.

The same sort of thing applies to nested lists of lists. What array adds is the view case.

d = np.array([np.array(x, copy=True) for x in array_of_arrays])

In this case you are making copies of the individual elements. As others noted there is a deepcopy function. It was designed for things like lists of lists, but works on arrays as well. It is basically doing what you do with d; recursively working down the nesting tree.

In general, an object array is like list nesting. A few operations cross the object boundary, e.g.

 array_of_arrays+1

but even this effectively is

np.array([x+1 for x in array_of_arrays])

One thing that a object array adds, compared to a list, is operations like reshape. array_of_arrays.reshape(3,1) makes it 2d; if it had 4 elements you could do array_of_arrays.reshape(2,2). Some times that's handy; other times it's a pain (it's harder to iterate).

๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ copy-python-deep-copy-shallow-copy
Deep Copy and Shallow Copy in Python - GeeksforGeeks
A deep copy creates a new compound object before inserting copies of the items found in the original into it in a recursive manner. It will first construct a new collection object and then recursively populate it with copies of the child objects ...
Published ย  February 5, 2026
๐ŸŒ
Dataquest
dataquest.io โ€บ blog โ€บ python-copy-list
How to Copy a List in Python (5 Techniques w/ Examples)
March 6, 2023 - As mentioned earlier, the recursive deep copy produces a truly independent copy of the original list, which is why the inner lists in the original and copied lists point to two different memory locations. Obviously, any changes made to the inner list of one won't be reflected in the other. This tutorial discussed several different ways for copying a list in Python, such as the assignment operator, list slicing syntax, list.copy(), copy.copy(), and copy.deepcopy functions.
๐ŸŒ
Note.nkmk.me
note.nkmk.me โ€บ home โ€บ python
Shallow and Deep Copy in Python: copy(), deepcopy() | note.nkmk.me
May 13, 2023 - l = [0, 1, [2, 3]] l_slice_deepcopy = copy.deepcopy(l[1:]) print(l_slice_deepcopy) # [1, [2, 3]] l[1] = 100 l[2][0] = 200 print(l) # [0, 100, [200, 3]] print(l_slice_deepcopy) # [1, [2, 3]] ... NumPy: Concatenate arrays with np.concatenate, np.stack, etc. Get File Timestamp in Python: os.stat, os.path.getmtime
๐ŸŒ
Real Python
realpython.com โ€บ python-copy
How to Copy Objects in Python: Shallow vs Deep Copy Explained โ€“ Real Python
April 21, 2025 - In this case, the "h" argument in the array.array() call specifies that the array will store numbers as two-byte signed integers. As you can see, a Python array aggregates scalar numbers into a flat sequence, whereas a list and tuple can contain deeply nested structures arranged in a particular way.
๐ŸŒ
Plain English
python.plainenglish.io โ€บ shallow-copy-and-deep-copy-in-python-numpy-8861e0870c5f
Shallow Copy and Deep Copy in Python NumPy: Python Data Science Complete Course
May 6, 2022 - How to make a new reference to the same memory location of the NumPy array or a stand-alone copy of the NumPy array, by using a shallow or deep copy.
๐ŸŒ
TestDriven.io
testdriven.io โ€บ tips โ€บ c55f0591-8fcf-473c-bb46-4fb1279b4d4a
Tips and Tricks - Python deep copy via copy.deepcopy() | TestDriven.io
import copy house = { "width": 12, "length": 8, "height": 3.5, "doors": [ {"type": " ENTRANCE", "width": 0.9, "height": 2.2}, {"type": "BACK DOOR", "width": 0.7, "height": 2.0}, ], } same_house = copy.deepcopy(house) print(house) print(same_house) print(house == same_house) """ {'width': 12, 'length': 8, 'height': 3.5, 'doors': [{'type': ' ENTRANCE', 'width': 0.9, 'height': 2.2}, {'type': 'BACK DOOR', 'width': 0.7, 'height': 2.0}]} {'width': 12, 'length': 8, 'height': 3.5, 'doors': [{'type': ' ENTRANCE', 'width': 0.9, 'height': 2.2}, {'type': 'BACK DOOR', 'width': 0.7, 'height': 2.0}]} True ""
๐ŸŒ
Vultr Docs
docs.vultr.com โ€บ python โ€บ third-party โ€บ numpy โ€บ copy
Python Numpy copy() - Create Array Copy | Vultr Docs
November 7, 2024 - The numpy.copy() function is a fundamental operation in Python's NumPy library, designed to create a copy of an array. This function is crucial when you need to preserve the original array's data while manipulating the copied array, ensuring ...
๐ŸŒ
Programiz
programiz.com โ€บ python-programming โ€บ shallow-deep-copy
Python Shallow Copy and Deep Copy (With Examples)
Here, the copy() return a shallow copy of x. Similarly, deepcopy() return a deep copy of x. A shallow copy creates a new object which stores the reference of the original elements. So, a shallow copy doesn't create a copy of nested objects, instead it just copies the reference of nested objects.
๐ŸŒ
GitHub
gist.github.com โ€บ a050c5b3e6a4866b8949
Deep Copy a 2d array in python ยท GitHub
Deep Copy a 2d array in python. GitHub Gist: instantly share code, notes, and snippets.
๐ŸŒ
Quora
quora.com โ€บ What-is-the-difference-between-a-shallow-and-deep-copy-of-an-array-in-Python
What is the difference between a shallow and deep copy of an array in Python? - Quora
Whereas In Deep copy, the item from both the nested lists NOT shares the same memory location i.e shares Different Memory location so changes are not reflects on one nested list by changes made on another nested list. Hope you find it useful !! ... Python values are stored as/in objects.
๐ŸŒ
Delft Stack
delftstack.com โ€บ home โ€บ howto โ€บ numpy โ€บ python numpy deep copy
NumPy Deep Copy | Delft Stack
March 11, 2025 - In this article, we will explore two main methods for achieving deep copies of NumPy arrays in Python: using the built-in copy.deepcopy() function and a user-defined approach.
๐ŸŒ
Aims
python.aims.ac.za โ€บ pages โ€บ cp_mutable_objs.html
21. Be careful: copying arrays, lists (and more) โ€” AIMS Python 0.7 documentation
It works, but that seems like a lot of code just to copy an array. Probably the safest and most efficient way to do this is to use Python's "copy" module. One of the functions there is called deepcopy(), and this will be our function of choice for copying objects that have this issue.