Shallow copy, why the list does not change

I am trying to understand the difference between shallow copy and deep copy in python. I read a lot of posts here and they were helpful. However, I still do not understand the difference. Can someone explain the reason for the result below. The result, which I do not understand, is indicated in the comments.

Thank you very much.

import copy import random class understand_copy(object): def __init__(self): self.listofvalues = [4, 5] def set_listofvalues(self, pos, value): self.listofvalues[pos] = value ins = understand_copy() newins = copy.copy(ins) newins.set_listofvalues(1,3) print "ins = ", ins.listofvalues print "in newins", newins.listofvalues # Gives the following output as I would expect based on the explanations. # prints ins = [4, 3] # prints newins = [4, 3] newins.listofvalues.append(5) print "ins =", ins.listofvalues print "newins =", newins.listofvalues # Gives the following output as I would expect based on the explanations. # prints ins = [4, 3, 5] # prints newins = [4, 3, 5] newins.listofvalues = [10, 11] print "ins = ", ins.listofvalues print "newins = ", newins.listofvalues # Gives # ins = [4, 3, 5] # newins = [10, 11] # This is the output that I do not understand. # Why does ins.listofvalues not change this case.** 
+4
source share
2 answers

In Python, an object field contains a reference to an object. Therefore, when you assign a new list in your example, you change the object referenced by the field, not its contents. Before changing the listofvalues property for an object that references the same list, but after affectation, they refer to two different lists.

This is equivalent to the following code:

 >>> a = [4, 5] >>> b = a >>> b.append(3) >>> b [4, 5, 3] >>> a [4, 5, 3] >>> b = [6, 7] >>> b [6, 7] >>> a [4, 5, 3] 

If you want to change the contents of the list, not the link, you need to use slicing. I.e:

 >>> a = [4, 5, 3] >>> b = a >>> b[:] = [6, 7] >>> b [6, 7] >>> a [6, 7] 

NOTE. The following is based on my understanding of Python 2.6's internal components. This, therefore, is implementation-specific, however the mental model that it gives you is probably pretty close to how the language rule is written and should work with any implementation.

In Python, objects are always referenced (as in Java, not C ++). However, the variable name or attribute name can be implemented as bindings in the dictionary as such in CPython (except for the optimization of local variables, the presence of __slots__ or pseudo-attributes opened through __getattr__ and friends).

In Python, each object is a private dictionary that matches each attribute name with a value. And the interpreter has two private dictionaries containing a comparison of local and global variables with their meaning. When you change the value of a variable or attribute of an object, you simply change the binding in the corresponding dictionary.

So, in your example, you have the same behavior as in the following code:

 def understand_copy(): return {'listofvalues': [4, 5]} def deepcopy(obj): if instance(obj, dict): copy = {} for key, value in obj.iteritems(): copy[key] = deepcopy(value) # Note the recursion return copy if instance(obj, list): copy = [] for value in obj: copy.append(deepcopy(value)) # Note the recursion return copy return obj def copy(obj): if instance(obj, dict): copy = {} for key, value in obj.iteritems(): copy[key] = value # No recursion this time, the copy is shallow return copy if instance(obj, list): copy = [] for value in obj: copy.append(value) # No recursion this time, the copy is shallow return copy return obj globals = {} globals['ins'] = understand_copy() globals['new'] = copy(global['ins']) # Now globals['ins']['listofvalues'] # and globals['new']['listofvalues'] # reference the same object! globals['ins']['listofvalues'].__setitem__(0, 3) globals['ins']['listofvalues'].append(5) # We are calling function on one object, # but not changing a binding, so the two # 'listofvalues' attribute still refers # to the same object. globals['ins']['listofvalues'] = [10, 11] # Now we changed the binding of the name # in the dictionary 'ins' so now the two # objects attributes points to different # lists. 
+7
source

ins.listofvalues โ€‹โ€‹does not change because you replaced the new attribute with a new list, and append () does not replace the object, modifying it. Therefore, two different attributes now point to different lists.

You can get the same effect without copying:

 >>> ins = [1,2,3] >>> newins = ins >>> >>> ins.append(4) >>> newins [1, 2, 3, 4] >>> >>> ins = [5,6,7] >>> newins [1, 2, 3, 4] 

So, you understand copy / deepcopy correctly, the problem is a common misunderstanding about how variables work in Python. These are links, but they are not pointers. It is usually best understood as "shortcuts." In this case, you designated the source list as ins.listofvalues, and then you copied this shortcut to newins.listofvalues. But when you reassign ins.listofvalues, you are attaching this label to a new object.

+3
source

Source: https://habr.com/ru/post/1333346/


All Articles