Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing elements of one list, in another list - by reference - in Python?

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])?

like image 564
sdaau Avatar asked May 22 '13 15:05

sdaau


2 Answers

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.

like image 72
mgilson Avatar answered Oct 12 '22 00:10

mgilson


>>> 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]
like image 25
Ashwini Chaudhary Avatar answered Oct 11 '22 22:10

Ashwini Chaudhary