Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a python list with one pointer pointing to two places

I have a python list:

x = [1,1,1]

I set y equal to that list and then change y, and x changes because I have two pointers to the same place in memory.

y = x
y[1] = 7
print x
[1, 7, 1]

That's all good. Is there anyway I can make a list of x+y so that when I change x, I also change y? Here's some code which doesn't work but maybe clarifies my goal:

q = x + y
print q
[1, 7, 1, 1, 7, 1]
q[0] = 2
print q
[2, 7, 1, 1, 7, 1]

but I'd LIKE q to instead become:

[2, 7, 1, 2, 7, 1]

I hope that's clear, and I hope even more that it's achievable!

EDIT: To respond to the inquiries as to why this would be useful, I intend to use it as a ring buffer. Say I want to take the contents at position p and move them to position p+1: Instead of:

if p+1 == len(q):
    q[0]= q[p]
else: 
    q[p+1] =q[p]

I could just do:

q[p+1] = q[p]
like image 264
TPM Avatar asked Jan 21 '26 03:01

TPM


2 Answers

This is doing the trick, even for deleting and inserting elements:

from collections import MutableSequence
from itertools import chain, islice

class ChainedListProxy(MutableSequence):
  def __init__(self, *lists):
    self._lists=lists

  def _resolve_element(self, index):
    """ returning list and subindex in that list """
    for l in self._lists:
      if index>=len(l):
        index-=len(l)
      else:
        return l, index
    raise IndexError('index out of range')

  def __getitem__(self, index):
    l, i=self._resolve_element(index)
    return l[i]

  def __delitem__(self, index):
    l, i=self._resolve_element(index)
    del l[i]

  def __setitem__(self, index, value):
    if isinstance(index, slice):
      indicies=index.indices(len(self))
    l, i=self._resolve_element(index)
    l[i]=value

  def insert(self, index, value):
    l, i=self._resolve_element(index)
    l.insert(i, value)

  def __len__(self):
    return sum( (len(l) for l in self._lists) )

Usage:

>>> x=[1,2,3]
>>> q=ChainedListProxy(x,x)
>>> q[0]
1
>>> q[3]=5
>>> q[0]
5
>>> list(q)
[5, 2, 3, 5, 2, 3]
like image 101
Günther Jena Avatar answered Jan 23 '26 18:01

Günther Jena


What you are asking for is not achievable without making a new object.

When you concatenate lists, you are not modifying the original lists. You are returning a completely new list that has no references attached to the original list.

You can somewhat implement that functionality by creating your own integer object. Currently x[0] and y[0] refer to the same place in memory. Since integers are immutable, adding x and y causes you to create new integers.

An example of the implementation I described above is here:

class myInt:
    def __init__(self, val):
        self.val = val
    def setval(self, new):
        self.val = new
    def __repr__(self):
        return str(self.val)
x = [myInt(0), myInt(1), myInt(2)]
y = x
z = x + y
print(z)
>>>[0, 1, 2, 0, 1, 2]
z[0].setval(10)
>>>[10, 1, 2, 10, 1, 2]
like image 42
Primusa Avatar answered Jan 23 '26 17:01

Primusa