from collections.abc import Sequence, MutableSequence class ChainedListView(Sequence): """A view of a chain of lists, treated as a single immutable list.""" def __init__(self, *lists): self.lists = list(lists) def __repr__(self): return "{}({})".format(type(self).__name__, ", ".join(self.lists)) def __str__(self): return str(list(self)) def __eq__(self, other): if len(self) != len(other): return False return all(item == other[i] for i, item in enumerate(self)) def __ne__(self, other): return not (self == other) # XXX could use other comparisons def __len__(self): return sum(len(lst) for lst in self.lists) def get_list(self, index): """Return (list, index) for the sublist where index falls.""" if isinstance(index, slice): # XXX needs support for slices raise NotImplementedError index_orig = index if index < 0: index = len(self) + index for lst in self.lists: if index < len(lst): return lst, index index = index - len(lst) raise IndexError(index_orig) def __getitem__(self, index): lst, index = self.get_list(index) return lst[index] class ChainedList(ChainedListView, MutableSequence): """A chain of lists, treated as a single list. If insert_upper is False and an index falls on the first element of a sublist, the item will actually be added to the end of the preceding sublist. """ def __init__(self, *lists, insert_upper=True): super(ChainedList, self).__init__(*lists) self.insert_upper = insert_upper def __setitem__(self, index, item): lst, index = self.get_list(index) lst[index] = item def __delitem__(self, index): lst, index = self.get_list(index) del lst[index] def insert(self, index, item): if index == len(self): lst, index = self.get_list(index-1) if self.insert_upper and lst is not self.lists[-1]: self.lists[self.lists.index(lst) + 1].append(item) else: lst.insert(index + 1, item) return # index < len(self) lst, index = self.get_list(index) if index == 0 and not self.insert_upper and lst is not self.lists[0]: self.lists[self.lists.index(lst) - 1].append(item) else: lst.insert(index, item)