سؤال

>>> a = [1,2,3]
>>> b = a[:]
>>> id(a[0]) == id(b[0])
True
>>> b[0] = 99
>>> id(a[0]) == id(b[0])
False

I understand that to make a shallow copy we can use slicing and also that there is a copy module. But why does writing to the index of "b" change the id.

هل كانت مفيدة؟

المحلول

Part 1: Why did calling b[0] = 99 cause a[0] == b[0] >> False?

The answer to your question is that when you do b[0] = 99, you do not "modify the memory address that one of B's fields point to", you actually change where one of B's fields point.

a = [1,2,3]  

Now a contains a reference to a list object, which in turn contains three references to three int objects.

b = a[:]

now b refers to a list object (not the same one as a refers to), and contains three references to three int objects, the same ones that a refers to.

id(a) == id(b)
#False

False, because a and b are references to different list objects.

id(a[0]) == id(b[0])
#True

True, because a[0] is a reference to the 1 object, and so is b[0]. There is only one 1 object, and it is referred to by a[0] and b[0]

b[0] = 99  
# ^^^^^^ THIS IS NOT WHAT THE WIKIPEDIA ARTICLE IS DESCRIBING

b still refers to the same old list object, but b[0] now refers to the 99 object. You have simply replaced one of the references in the list referred to by b with a new reference to a different object. You have not modified any objects that a points to!

id(a[0]) == id(b[0]) 
#False

False, because the 1 object is not the same object as the 99 object.


Part 2: What the heck is that wikipedia article talking about, then?

Here's an example of a copy operation where you are actually "modifying the memory address that one of B's fields point to", so you can see the subsequent change in the copied object.

a = [[1],[2],[3]]

a is a reference to a list object, but now the list object contains three references to list objects, which each contain a reference to an int object.

b = a[:]

As before, you've made b refer to a new, different list object, which refers to the SAME three list objects as are referred to in a. Here's proof:

id(a) == id(b)
# False

False, because as before a and b are references to different list objects.

id(a[0]) == id(b[0]) 
#True

True, because as before, both a[0] and b[0] refer to the same object. This time it's a list object, which is not immutable, unlike an int object - we can actually change the contents of the list object! Here's where it gets different:

b[0][0] = 99  

We did it - we changed the contents of the list object referred to by b[0]

a[0][0]
# 99 !!!!!!!!!!!!!!  Wikipedia doesn't need to be edited!

See? The 'list' referred to by the list referred to by a now refers to the 99 object, rather than the 1 object, because it's the same list that you accessed with b[0][0] = 99. Simple, huh ;)

id(a[0]) == id(b[0])
#True !!!

True, because although we changed the contents of what a[0] referenced, we did not change the reference itself in a[0] and b[0] - this is closer to what the Wikipedia article is describing in the 'Shallow Copy' section.

نصائح أخرى

But why does writing to the index of "b" change the id.

Because they're different things now. If you were to check a, a[0] is still 1 rather than 99 because b was a copy. If you didn't want this behavior, you wouldn't do the copy:

>>> a = [1,2,3]
>>> b = a
>>> b[0] = 99
>>> a[0]
99

instead, you have this:

>>> a = [1,2,3]
>>> b = a[:]
>>> b[0] = 99
>>> a[0]
1

Since you tagged deepcopy...it's something that only matters if your lists themselves contain mutable arguments. Say, for instance, you had:

>>> from copy import deepcopy
>>> a = [[1,2],[3,4]]
>>> b = a
>>> c = a[:]
>>> d = deepcopy(a)

So a is b, c is a shallow copy, and d is a deep copy.

>>> b[0] = 3
>>> a
[3, [3,4]]
>>> c
[[1,2], [3,4]]
>>> d
[[1,2], [3,4]]

b is the same as a, but the copies were unaffected.

>>> c[1][1] = 'hi'
>>> a
[3, [3, 'hi']]
>>> c
[[1, 2], [3, 'hi']]

If you replace the entries of c, a is unaffected. But if you still have the original lists within the entries, modifying one still shows up in the other. The nested lists are still the same.

>>> d[1][1] = 10
>>> a
[3, [3, 'hi']]
>>> d
[[1, 2], [3, 10]]

Since d was a deep copy, it copied the list as well as its nested lists, so we can modify it and its elements at will without worry about messing up the other copies.

When you made a shallow copy of a to b ( b = a[:] ) you duplicated it. b became a copy of a at a specific point in time -- it's a "snapshot" of a.

when you updated b[0] = 99 , you updated b -- the copy of a. you didn't update a. that's the point of making a shallow (or deep) copy -- you want a new variable that has the same contents, this way you can make changes to the copy without affecting the original.

if you want b[0] = 99 to affect a as well, then you don't want to "copy" a, you just want to refer to it by another name. you'd use b = a.

A list in Python contains references to its contents.

When you copy a list, using a[:] or the copy method, you create a new list with the same references. What happens when you change an item of the original list depends on its type.

Mutable objects, such as lists, can be changed in-place (that's what makes them mutable). Both lists still refer to the same object:

a = [[0],[1],[2]]
b = a[:]
a[0].append[1]
b[0] # >>> [0,1]

Integers, on the other hand, are immutable. Changing an integer creates a new object, with a new id and reference.

a = [0, 1, 2]
b = a[:]
a[0] = 10
b[0] # >>> 0
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top