I just thought I'd jot this down now that I've seen it - it would be nice to get a confirmation on this behavior; I did see How do I pass a variable by reference?, but I'm not sure how to interpret it in this context.
Let's say we have these two arrays/lists:
a = [1, 2, 3, 4]
b = [-1, a, -100, a[2], -1]
The interpreter initially sees them as:
>>> print(a)
[1, 2, 3, 4]
>>> print(b)
[-1, [1, 2, 3, 4], -100, 3, -1]
Now let's change a[2]
, and see what happens:
>>> print(a)
[1, 2, 55, 4]
>>> print(b)
[-1, [1, 2, 55, 4], -100, 3, -1]
So, wherever list b
has a reference to the list a
, the value has been updated - but wherever b
was initialized with (a reference to?) an element from list a
, it seems that Python expanded the value at initialization time, and thus stored the element by value (not by reference), so it's value obviously doesn't update.
Basically, I found a use case, where it would be convenient to be able to define e.g. b = [-1 a[2] -1]
, and then update a[2]
, and be able to count that the latest value of a[2]
will be emitted when getting the value of (in this case) b[1]
. Is there a way to do that in Python, without having to do b = [-1 a -1]
, and then reading b[1][2]
(I'd like to get the value of a[2]
just by using b[1]
)?
a
is a reference to a mutable list. So, when you say:
a[2] = 55
you are calling __setitem__
on the list which sets an item in the list. list.__setitem__
doesn't make any attempt to mutate the item that used to be stored in the second index. It simply replaces that reference with a new one.
On the other side, x = a[2]
calls __getitem__
which just creates a new reference to the object stored at that index in the list.
>>> a = [1000,2000,3000,4000]
>>> sys.getrefcount(a[2])
2
>>> b = [-1, a, -100, a[2], -1]
>>> a is b[1] # b[1] and `a` are actually two variables pointing to the same object
True
#[1000,2000,3000,4000] can be accessed or modified by either `a` or `b[1]`
>>> sys.getrefcount(a)
3
>>> sys.getrefcount(a[2])
3
Now there are total 3 references to the object 3000 in memory(a[2]
,b[-2]
and shell itself), but as integers are immutable so if you change of modify a[2]
it'll simply remove one reference from the object 3000, But b[-2]
will still point to the same object in memory and a[2]
will now point to some newly assigned object.
>>> id(a[2]),id(b[-2])
(150561920, 150561920)
>>> a[-2] = 5
>>> id(a[2]),id(b[-2]) #b still points to the same object
(148751024, 150561920)
>>> sys.getrefcount(b[-2])
2
If the item at a[2]
is a mutable object, say list
:
>>> a = [1000,2000, [2] , 4000]
>>> b = [-1, a, -100, a[2], -1]
>>> a[2] += [5] # we can modify [2] from from either a[2] or b[-2]
>>> b[-2]+= [10] # `+=` , `list.extend`, `list.append` changes the list in-place
>>> a[2] is b[-2] #both still points to the same object as lists are mutable
True
>>> a
[1000, 2000, [2, 5, 10], 4000]
>>> b
[-1, [1000, 2000, [2, 5, 10], 4000], -100, [2, 5, 10], -1]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With