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

I just thought that I would write it down when I saw it - it would be nice to get confirmation of this behavior; I saw How to 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] 

At first, the interpreter sees them as:

 >>> print(a) [1, 2, 3, 4] >>> print(b) [-1, [1, 2, 3, 4], -100, 3, -1] 

Now 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 link to list a , the value has been updated, but wherever b was an initialized (link to?) Element from list a , it seems like Python expanded the value during initialization and thus saved the element by value (not by reference), so its value is not explicitly updated.

Basically, I found a use case where it would be convenient to define, for example. b = [-1 a[2] -1] , and then update a[2] and take into account that the last value of a[2] will be issued when the value (in this case) b[1] received. Is there a way to do this in Python, without having to do b = [-1 a -1] and then read b[1][2] (I would like to get the value of a[2] only with b[1] )?

+6
source share
3 answers

a - link to the mutable list. So when you say:

 a[2] = 55 

you call __setitem__ on the list that sets the item in the list. list.__setitem__ no attempt to modify an item that was previously stored in the second index. It just replaces this link with a new one.

On the other hand, x = a[2] calls __getitem__ , which simply creates a new reference to the object stored in this index in the list.

+2
source
 >>> 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 3 references to object 3000 in memory ( a[2] , b[-2] and the shell itself), but since the integers are immutable, so if you change the change of a[2] , it will simply delete one link from 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 element in 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] 
+1
source

To solve the problem of accessing sublists by the index of an external list, you can use something like:

 class RecursiveList(list): def getitem(self, index, recurse=True): if not recurse: return self[index] else: return list(RecursiveList.flatten(self))[index] @staticmethod def flatten(l): for item in l: if hasattr(item, "__iter__"): for v in RecursiveList.flatten(item): yield v else: yield item 

To get the exact behavior you request, add the following:

  def __getitem__(self, i): return self.getitem(i, true) 

Note that this is likely to break if you try to use slices.

0
source

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


All Articles