Handy for composing lists without losing the underlying indepedence. The "lists" don't actually have to be lists, though for ChainedList they must be mutable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 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)
|
As-is runs only on Python 3.3, though it should be easy to back-port.