-1

"Python usually just assigns. Some commands produce a shallow copy (e.g., slicing, sorted(), dict .get(), ...). A deep copy is produced by copy.deepcopy, np.array and some other commands."

How to improve that quote (of mine)? Mainly the second and third sentences require improving: how do you know (without always testing everything) which commands produce a shallow copy, which a deep copy, and which no copy (i.e., refer to the original memory area)?

The rest below is just an elaboration of the above.

Some more examples: The dictionary methods .pop(), and .popitem() produce the original reference (no copy). list2 = list1 (assignment) produces the original; i.e., both variable names point to the same memory object.

An ideal answer (or linked page) might provide a longer list of principles (and maybe also a list of nontrivial examples).

.append(): c.append(d) adds to c a new pointer memory area slot that points to where d now points. In that sense, c becomes a shallow copy of the sequence of appended elements (see below) but any such new c[k] is not a copy. When c[k] exists, c[k]=d has the same effect.

Similarly, new class instances reserve new pointer memory areas but they typically get assigned to something old.

Some examples (I was asked to illustrate "original"):

x = [1, 2, 3]
y = x  #"original": x & y have exactly the same contents (they point to the same memory area)
y[0] = 9  #Now x == [9, 2, 3] == y

import copy
z = copy.copy(x) #shallow copy, but no difference to deep copy here. (Completely independent from x, y.)
z[2] = 999
print(z) #[9, 2, 999]
print(x) #[9, 2, 3]

class LTest():
    def __init__(self,li):
        self.list = li
li1 = [1, 2, 3]
lT = LTest(li1)   #this is a new object, but lT.list just gets assigned to li1
#(i.e., they now point to the same memory area (pointer array))
li1[0] = 99
print(lT.list)  #[99, 2, 3]
li1 = [4, 5, 6]  #makes li1 point to this newly created pointer array;
# does not affect the contents of the old pointer array (still pointed to by lT.list)
print(lT.list)  #[99, 2, 3]

a=[[0, 0], [1, 5], [2, -5]]
b = sorted(a,key=lambda a:a[1]) #shallow "sorted copy" of a
# b[0][1]=9 would change a but b[2]=[7,8] would not change a.
c = []  #we'll make c a shallow copy of b (hence "of a")
for j in range(len(b)):
    c.append(b[j])  #a new pointer slot in the c memory area, but it points to where b[j] pointed
# b[0][1]=9 would change a & c but b[2]=[7,8] would not change either.
Convexity
  • 99
  • 3
  • 1
    You could try all these methods by yourself. – Itération 122442 Aug 16 '23 at 11:52
  • Please also provide the difference between 'original' and 'shallowcopy'. – Captain Trojan Aug 16 '23 at 11:56
  • 3
    Which `.get()` do you mean? – mkrieger1 Aug 16 '23 at 12:22
  • I'm hoping for a better principle than that in the quote plus maybe also some nontrivial illustrative examples in each category (e.g., those I listed or some others, whichever works best). Either or both parts could also be links to external pages (but hopefully at least some basic principles would be given here). – Convexity Aug 16 '23 at 12:22
  • "This question already has answers here" links to page that defines the three concepts. My question was, instead: "There are a zillion commands etc. in Python. How do I know, which of them produce a deep copy, which a shallow copy and which the original." – Convexity Aug 17 '23 at 12:43

0 Answers0