Make a list out of two (or more) lists which refers to the original lists when appending new element
I would like to create a list of objects which stands for the whole category of objects with the same attribute (let's say the objects that are visible). But once I append the object to one of the subgroups it doesn't update the list of all visible objects (visible_objects). This is known Python behaviour to me but I would like to know what is the best practice to implement this so it appends the element also to the combined list (visible_objects) in a clean and seamless way. (i.e. everytime I append new element to one of the subgroups)
class A:
def __init__(self,i):
self.i=i
images=[A(1),A(2),A(3)]
images2=[A(4),A(5)]
visible_objects=images+images2
images.append(A(6))
print([x.i for x in images])
print([x.i for x in images2])
print([x.i for x in visible_objects])
[1, 2, 3, 6]
[4, 5]
[1, 2, 3, 4, 5]
I want the last list also contain 6 without reassigning visible_objects after every append. Thank you very much.
2 answers
-
answered 2021-02-23 17:13
DovaX
Ok, so I think I kind of solved it but I'm not sure it is the best approach.
class A: def __init__(self,i): self.i=i class MagicList: def __init__(self,elements=[],referenced_lists=None): self.elements=elements self.referenced_lists=referenced_lists self.embedded_in_lists=[] if self.referenced_lists is not None: self.elements=[] for i,magic_list in enumerate(self.referenced_lists): for item in magic_list.elements: self.elements.append(item) self.referenced_lists[i].embedded_in_lists.append(self) def append(self,obj): self.elements.append(obj) for i,list1 in enumerate(self.embedded_in_lists): self.embedded_in_lists[i].elements=[] for j in range(len(list1.referenced_lists)): self.embedded_in_lists[i].elements+=list1.referenced_lists[j].elements def pop(self,index): self.elements.pop(index) for i,item in enumerate(self.embedded_in_lists): item.elements.pop(index) def __str__(self): return(str(self.elements)) m=MagicList([1,3,4]) m2=MagicList([2,5,8]) m3=MagicList(referenced_lists=[m,m2]) print(m) print(m2) print(m3) images=MagicList([A(1),A(2),A(3)]) images2=MagicList([A(4),A(5)]) visible_objects=MagicList(referenced_lists=[images,images2]) print([x.i for x in images.elements]) print([x.i for x in visible_objects.elements]) images.append(A(6)) print([x.i for x in images.elements]) print([x.i for x in visible_objects.elements]) images.pop(3) print([x.i for x in images.elements]) print([x.i for x in visible_objects.elements])
Result:
[1, 3, 4] [2, 5, 8] [1, 3, 4, 2, 5, 8] [1, 2, 3] [1, 2, 3, 4, 5] [1, 2, 3, 6] [1, 2, 3, 6, 4, 5] [1, 2, 3] [1, 2, 3, 4, 5]
-
answered 2021-02-23 17:32
Pranav Hosangadi
Your answer is a good approach, but you could also do this by defining a
__getitem__()
inMagicList
that returns the correct element. This way, you don't need to spend time cloning or copying elements from each list to a big "super-list", and modifications to the "sub-lists" are seamlessly reflected when you try to get an index of theMagicList
object.class MagicList(): def __init__(self, *referenced_lists): self.lists = referenced_lists def __getitem__(self, index): cum_len = 0 for sublist in self.lists: sublist_start_index = cum_len sublist_end_index = cum_len + len(sublist) if sublist_end_index > index: return sublist[index - sublist_start_index] cum_len = sublist_end_index raise IndexError("list index out of range") def __str__(self): return str([x for x in self]) def add_list(self, new_list): if isinstance(new_list, list): # If new_list is a list, append it to self.lists self.lists.append(new_list) else: # Try to convert new_list to a list and append that try: self.lists.append(list(new_list)) except TypeError: # new_list is not an iterable, so list(new_list) throws a TypeError self.lists.append([new_list])
Then, you can do:
l1 = [1, 2, 3, 4, 5] l2 = [100, 200, 300, 400, 500] m = MagicList(l1, l2) # You can give this any number of lists print("Original list: ") print(m) l1.append(-1) print("Appending to l1: ") print(m) l2.append(1000) print("Appending to l2: ") print(m) l1[0] = 'abc' print("Modifying l1: ") print(m) l2[-2] = 'def' print("Modifying l2: ") print(m) del l1[0] print("Removing from l1: ") print(m) del l2[-1] print("Removing from l2: ") print(m)
This gives the output:
Original list: [1, 2, 3, 4, 5, 100, 200, 300, 400, 500] Appending to l1: [1, 2, 3, 4, 5, -1, 100, 200, 300, 400, 500] Appending to l2: [1, 2, 3, 4, 5, -1, 100, 200, 300, 400, 500, 1000] Modifying l1: ['abc', 2, 3, 4, 5, -1, 100, 200, 300, 400, 500, 1000] Modifying l2: ['abc', 2, 3, 4, 5, -1, 100, 200, 300, 400, 'def', 1000] Removing from l1: [2, 3, 4, 5, -1, 100, 200, 300, 400, 'def', 1000] Removing from l2: [2, 3, 4, 5, -1, 100, 200, 300, 400, 'def']
Something to remember though: if you want to append / insert anything to
MagicList
that isn't in the referenced lists, you need to wrap that in a list.