Official documentation: Copy topic
Source code: Lib/copy.py
However, there are already many articles about shallow and deep copies of Python on the web, but many of them seem to be vaguely familiar, so I decided to write this article with my own understanding.
When someone mentions copying in Python, do you immediately stand up and say, “I will”? Here’s what happens:
import copy
x = copy.copy(y) # Shallow copy I can
x = copy.deepcopy(y) # Deep copy I'm coming
Copy the code
What’s the difference between a shallow copy and a deep copy? Can you tell me?
Start with reference vs. copy
First, we need to understand what object reference and object copy (copy) are.
Object reference
An assignment to an object in Python is simply a reference to an object. When you create an object and assign it to another variable, Python does not copy the object, only the reference to the object.
>>> a = 1
>>> b = a
>>> id(a) == id(b)
True
>>> x = [1.2.3]
>>> y = [x, 4]
>>> x
[1.2.3]
>>> y
[[1.2.3].4]
>>>
>>>> id(x) == id(y)
False
>>> id(x) == id(y[0])
True
Copy the code
If this process doesn’t make sense, take a look at the following image:
When we operate on the x list, something unexpected happens to y as well:
>>> x[1] = 2020
>>> y
[[1.2020.3].4]
Copy the code
Since lists are mutable, when you modify the list object x, you also change the reference to x in object Y.
So it’s important to note that when we modify a mutable object in place we may affect other references to the same object elsewhere in the program. If you don’t want to do this, you need to explicitly tell Python to copy the object.
Copy the object
If you need to copy it, you can do the following:
- Sharding expressions without constraints (
L[:]
) - Factory functions (e.g. List /dir/set)
- Dictionary copy method (
X.copy()
) - Copy Standard library module (
import copy
)
For example, suppose we have a list L and a dictionary D:
>>> L = [2019.2020.2021]
>>> D = {'1':2019.'2':2020.'3':2021}
>>>
>>> A = L[:] A= List(L)
>>> B = D.copy() # to distinguish B = D
>>> A
[2019.2020.2021]
>>> B
{'1': 2019.'2': 2020.'3': 2021}
Copy the code
Once you’ve defined it this way, when you modify A and B, it doesn’t affect the original L and D, because it’s just A copy of the object.
>>> A[1] = 'happy'
>>> B[3] = 'today'
>>> L, D
([2019.2020.2021] and {'1': 2019.'2': 2020.'3': 2021})
>>> A, B
([2019.'happy'.2021] and {'1': 2019.'2': 2020.'3': 2021.3: 'today'})
Copy the code
The above list and dictionary copy operations are shallow copies by default:
- Shallow copies of dictionaries can be made using the dict.copy() method
- Making a shallow copy of a list can be done by assigning slices of the entire list, for example, copied_list = original_list[:].
At this point, the question arises, right? What is shallow copy? What about shallow copies versus deep copies?
Talk about shallow copy and deep copy
Official document definition:
The difference between shallow and deep copy only relates to composite objects (that is, objects that contain other objects, such as lists or instances of classes) :
A shallow copy constructs a new composite object and (to the extent possible) inserts references found in the original object.
A deep copy constructs a new composite object and then recursively inserts a copy of the object found in the original.
Shallow copy
Shallow copy: the outermost object itself is copied. The inner elements are copied only as references. That is, I copy the object, but I don’t copy any other objects that are referenced in that object.
In layman’s terms, you have a 🧺 (basket) 🥚 (eggs) in your cupboard, and then make a shallow copy of it. I only copied the outermost cabinet, as for the inside elements (🧺 and 🥚) I did not copy.
When we encounter simple objects, the above explanation seems to make sense; If you encounter a compound object, for example:
l1 = [3[66.55.44], (3.7.21)]
l2 = list(l1)
l1.append(100)
print('l1:', l1)
print('l2:', l2)
l1[1].remove(55)
l2[1=] + [33.22]
l2[2] + = (9.9.81)
print('l1:', l1)
print('l2:', l2)
Copy the code
Code explanation:
l2
isl1
The shallow copy- I append 100 to PI
l1
forl2
Have no effect onl1
The internal listl1[1
55 is deleted from, yesl2
Also have an impact becausel1[1]
andl2[1]
It’s bound to the same list- For mutable objects,
l2[1
The list referenced is += in place to modify the list. This modification resulted inl1[1]
Things have changed- For tuples, the += operator creates a new tuple and then rebinds to the variable L2 [2]. This is the equivalent
l2[2] = l2[2] + (10, 11)
. Now,l1
和l2
The tuples in the last position are not the same object
Visualize this code as follows:
Give it a try. You can click here
Deep copy
Deep copy: Both outer and inner elements copy the object itself, not references. That is, I copy the object, and I copy the other objects that are referenced in that object.
Compare the basket with the egg: your cupboard contains a 🧺 (basket) 🥚 (egg), then copy the meaning deeply. Copy the outermost cabinet and the interior elements (🧺 and 🥚).
from copy import deepcopy
l1 = [3[66.55.44], (3.7.21)]
l2 = deepcopy(l1)
l1.append(100)
print('l1:', l1)
print('l2:', l2)
l1[1].remove(55)
l2[1=] + [33.22]
l2[2] + = (9.9.81)
print('l1:', l1)
print('l2:', l2)
Copy the code
Output result:
Characteristics of copy
-
Objects of immutable type (such as numbers, strings, and other ‘atomic’ types) have no effect on depth copy. The final address value and value are equal. Copy (obj)”, “obj is copy. Deepcopy (obj)”
-
Variable type objects = Shallow copy: equal values but different addresses Copy Shallow copy: equal values but different addresses DeepCopy: equal values but different addresses
-
If the object has a circular reference, this naive algorithm goes into an infinite loop. The deepCopy function remembers the objects that have been copied, so it handles circular references gracefully.
Circular reference: B references A and appends to A; Deepcopy will try to copy A, and copy will go into an infinite loop. Such as the following code:
from copy import deepcopy, copy
a = [80.90]
b = [a, 100]
a.append(b)
print("a:", a)
print("b:", b)
c = deepcopy(a)
print("c:", c)
d = copy(b)
print("d:", d)
Copy the code
Output result:
a: [80.90And [[...],100]]
b: [[80.90[...]. ].100]
c: [80.90And [[...],100]]
d: [[80.90And [[...],100]], 100]
Copy the code
Depth copy function
1. Reduce the use of memory. 2. 3. The replication behavior can be customized and controlled by implementing __copy() and __deep__() methods.
conclusion
After reading this article, turn to your deskmate and say: “X, I heard that you are learning Python recently. Do you know about shallow copy and deep copy?” “I don’t know. I’m a little dizzy.”
Copying is something we might not know we’ve used in the first few operations (the first three), and shallow copying is Python’s default copy mode. The copying method is as follows:
- Variable type slicing operations:
[:]
- Factory functions (e.g. List /dir/set)
- Dictionary copy method (
X.copy()
) - Then there is Python’s special copy library module: it contains two methods
copy()
anddeepcopy()
Shallow copy is like I only copy the outermost object, and I don’t copy the other objects referenced in the object. A deep copy is a complete copy of an object and its contents. Purpose of copy:
- To save memory
- Prevent data loss.
Postscript: Deep and shallow copy pits and difficult points are only on composite objects, simple objects are what we normally understand as copy. Objects that are not container types (such as numbers, strings, and other ‘atomic’ types) are not copied.
If your deskmate still doesn’t understand, you can throw this article to him so he can read it. If you think this article is good, please like it or hide it, or even better, follow it.
The content of this article has reference:
- Visualize how your code works
- “Smooth Python.” Chapter 8 object references, Variability, and garbage Collection
- Python3 library — 2.9 copy — Duplicate Objects