Python: creating subscriptions without copying

I have a question on how to create a sublist (I hope this is the right term to use) from this list without copying.

It seems that slicing can create sublists, but does it with copying. Here is an example.

In [1]: a = [1,2,3] In [2]: id(a) Out[2]: 4354651128 In [3]: b = a[0:2] In [4]: b Out[4]: [1, 2] In [5]: id(b) Out[5]: 4354621312 In [6]: id(a[0:2]) Out[6]: 4354620880 

See here id of b and a [0: 2] are different, although their values ​​are the same. To double check, change the value in a, the value in b will not change.

 In [7]: a[1] = 4 In [8]: a Out[8]: [1, 4, 3] In [9]: b Out[9]: [1, 2] 

So, to get back to my question, how can I create sub-lists, but without copying? I mean, when the value of a [1] is set to 4, b will be [1, 4].

I searched around and did not find much help (maybe I am not using the right keywords). Thanks!


Changes:

Thank you all for your comments and answers! Here is what I learned.

  • In Python, there is no built-in way to create a list view (or create a sublist without copying).
  • The easiest way to do this is to use a numpy array.
  • Although the numpy array has data type limitations compared to a list, it does my job (to implement quicksort without extra memory).

Here is the same process with a numpy array.

 In [1]: import numpy as np In [2]: a = np.arange(1,4) In [3]: a Out[3]: array([1, 2, 3]) In [4]: b = a[0:2] In [5]: b Out[5]: array([1, 2]) In [6]: id(b) Out[6]: 4361253952 In [7]: id(a[0:2]) Out[7]: 4361254032 In [8]: a[1] = 4 In [9]: a Out[9]: array([1, 4, 3]) In [10]: b Out[10]: array([1, 4]) 
+5
source share
4 answers
Array objects

numpy supports this concept of creating interdependent sub-lists, instead of copying views data, instead of copying data.

Modifying the original numpy array will change the views created from the array, and changes to any of the views will also be reflected in the original array. Especially for large datasets, views are a great way to cut data in different ways while preserving memory.

 >>> import numpy as np >>> array1 = np.array([1, 2, 3, 4]) >>> view1 = array1[1:] >>> view1 array([2, 3, 4]) >>> view1[1] = 5 >>> view1 array([2, 5, 4]) >>> array1 array([1, 2, 5, 4]) # Notice that the change to view1 has been reflected in array1 

For more information, see the numpy documentation on views , as well as this SO post .

+4
source

you cannot if you slice a to get b.

All slice operations return a new list containing the requested items. This means that the following fragment returns a new (shallow) copy of the list [1]

[1] https://docs.python.org/2/tutorial/introduction.html

+1
source

There is no built-in way to do this. You can create your own list-like class that references the list and reuses all methods of accessing the list to work on it.

+1
source

There is no way to do this with the built-in Python data structures. However, I created a class that does what you need. I cannot guarantee that this will be a mistake, but you need to start.

 from itertools import islice class SubLister(object): def __init__(self, base=[], start=0, end=None): self._base = base self._start = start self._end = end def __len__(self): if self._end is None: return len(self._base) - self._start return self._end - self._start def __getitem__(self, index): self._check_end_range(index) return self._base[index + self._start] def __setitem__(self, index, value): self._check_end_range(index, "list assignment index out of range") self._base[index + self._start] = value def __delitem__(self, index): self._check_end_range(index, "list assignment index out of range") del self._base[index + self._start] def __iter__(self): return islice(self._base, self._start, self._end) def __str__(self): return str(self._base[self._start:self._end]) def __repr__(self): return repr(self._base[self._start:self._end]) # ...etc... def get_sublist(self, start=0, end=None): return SubLister(base=self._base, start=start, end=end) def _check_end_range(self, index, msg="list index out of range"): if self._end is not None and index >= self._end - self._start: raise IndexError(msg) 

Example:

 >>> from sublister import SubLister >>> base = SubLister([1, 2, 3, 4, 5]) >>> a = base.get_sublist(0, 2) >>> b = base.get_sublist(1) >>> base [1, 2, 3, 4, 5] >>> a [1, 2] >>> b [2, 3, 4, 5] >>> len(base) 5 >>> len(a) 2 >>> len(b) 4 >>> base[1] = 'ref' >>> base [1, 'ref', 3, 4, 5] >>> a [1, 'ref'] >>> b ['ref', 3, 4, 5] 
+1
source

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


All Articles