c 20082009 aryeh leib taurog released under the new bsd license this module contains a base type which provides liststyle mutations without specific data storage methods see also http static aryehleib comoldsitemutablelists html aryeh leib taurog a base class which provides complete list interface derived classes must call listmixin s init function and implement the following function getsingleexternalself i return single item with index i for general use the index i will always satisfy 0 i lenself function getsingleinternalself i same as above but for use within the class optional note that if getsingleinternal and getsingleinternal return different types of objects setlist must distinguish between the two and handle each appropriately function setlistself length items recreate the entire object note items may be a generator which calls getsingleinternal therefore it is necessary to cache the values in a temporary temp listitems before clobbering the original storage function setsingleself i value set the single item at index i to value optional if left undefined all mutations will result in rebuilding the object using setlist function lenself return the length int minlength the minimum legal length optional int maxlength the maximum legal length optional type or tuple allowed a type or tuple of allowed item types optional python initialization and special list interface methods calculate new length and dimensions special methods for arithmetic operations self must be shorter self must be shorter public list interface methods nonmutating mutating private routines careful index step and step are not the same step will never be none extended slice only allow assigning slice of same size we re not changing the length of the sequence extended slice only allow assigning slice of same size c 2008 2009 aryeh leib taurog released under the new bsd license this module contains a base type which provides list style mutations without specific data storage methods see also http static aryehleib com oldsite mutablelists html aryeh leib taurog a base class which provides complete list interface derived classes must call listmixin s __init__ function and implement the following function _get_single_external self i return single item with index i for general use the index i will always satisfy 0 i len self function _get_single_internal self i same as above but for use within the class optional note that if _get_single_internal and _get_single_internal return different types of objects _set_list must distinguish between the two and handle each appropriately function _set_list self length items recreate the entire object note items may be a generator which calls _get_single_internal therefore it is necessary to cache the values in a temporary temp list items before clobbering the original storage function _set_single self i value set the single item at index i to value optional if left undefined all mutations will result in rebuilding the object using _set_list function __len__ self return the length int _minlength the minimum legal length optional int _maxlength the maximum legal length optional type or tuple _allowed a type or tuple of allowed item types optional python initialization and special list interface methods calculate new length and dimensions special methods for arithmetic operations self must be shorter self must be shorter public list interface methods non mutating mutating private routines careful index step and step are not the same step will never be none extended slice only allow assigning slice of same size we re not changing the length of the sequence extended slice only allow assigning slice of same size
from functools import total_ordering


@total_ordering
class ListMixin:
    

    _minlength = 0
    _maxlength = None

    

    def __init__(self, *args, **kwargs):
        if not hasattr(self, "_get_single_internal"):
            self._get_single_internal = self._get_single_external

        if not hasattr(self, "_set_single"):
            self._set_single = self._set_single_rebuild
            self._assign_extended_slice = self._assign_extended_slice_rebuild

        super().__init__(*args, **kwargs)

    def __getitem__(self, index):
        "Get the item(s) at the specified index/slice."
        if isinstance(index, slice):
            return [
                self._get_single_external(i) for i in range(*index.indices(len(self)))
            ]
        else:
            index = self._checkindex(index)
            return self._get_single_external(index)

    def __delitem__(self, index):
        "Delete the item(s) at the specified index/slice."
        if not isinstance(index, (int, slice)):
            raise TypeError("%s is not a legal index" % index)

        
        origLen = len(self)
        if isinstance(index, int):
            index = self._checkindex(index)
            indexRange = [index]
        else:
            indexRange = range(*index.indices(origLen))

        newLen = origLen - len(indexRange)
        newItems = (
            self._get_single_internal(i) for i in range(origLen) if i not in indexRange
        )

        self._rebuild(newLen, newItems)

    def __setitem__(self, index, val):
        "Set the item(s) at the specified index/slice."
        if isinstance(index, slice):
            self._set_slice(index, val)
        else:
            index = self._checkindex(index)
            self._check_allowed((val,))
            self._set_single(index, val)

    
    def __add__(self, other):
        "add another list-like object"
        return self.__class__([*self, *other])

    def __radd__(self, other):
        "add to another list-like object"
        return other.__class__([*other, *self])

    def __iadd__(self, other):
        "add another list-like object to self"
        self.extend(other)
        return self

    def __mul__(self, n):
        "multiply"
        return self.__class__(list(self) * n)

    def __rmul__(self, n):
        "multiply"
        return self.__class__(list(self) * n)

    def __imul__(self, n):
        "multiply"
        if n <= 0:
            del self[:]
        else:
            cache = list(self)
            for i in range(n - 1):
                self.extend(cache)
        return self

    def __eq__(self, other):
        olen = len(other)
        for i in range(olen):
            try:
                c = self[i] == other[i]
            except IndexError:
                
                return False
            if not c:
                return False
        return len(self) == olen

    def __lt__(self, other):
        olen = len(other)
        for i in range(olen):
            try:
                c = self[i] < other[i]
            except IndexError:
                
                return True
            if c:
                return c
            elif other[i] < self[i]:
                return False
        return len(self) < olen

    
    
    def count(self, val):
        "Standard list count method"
        count = 0
        for i in self:
            if val == i:
                count += 1
        return count

    def index(self, val):
        "Standard list index method"
        for i in range(0, len(self)):
            if self[i] == val:
                return i
        raise ValueError("%s not found in object" % val)

    
    def append(self, val):
        "Standard list append method"
        self[len(self) :] = [val]

    def extend(self, vals):
        "Standard list extend method"
        self[len(self) :] = vals

    def insert(self, index, val):
        "Standard list insert method"
        if not isinstance(index, int):
            raise TypeError("%s is not a legal index" % index)
        self[index:index] = [val]

    def pop(self, index=-1):
        "Standard list pop method"
        result = self[index]
        del self[index]
        return result

    def remove(self, val):
        "Standard list remove method"
        del self[self.index(val)]

    def reverse(self):
        "Standard list reverse method"
        self[:] = self[-1::-1]

    def sort(self, key=None, reverse=False):
        "Standard list sort method"
        self[:] = sorted(self, key=key, reverse=reverse)

    
    def _rebuild(self, newLen, newItems):
        if newLen and newLen < self._minlength:
            raise ValueError("Must have at least %d items" % self._minlength)
        if self._maxlength is not None and newLen > self._maxlength:
            raise ValueError("Cannot have more than %d items" % self._maxlength)

        self._set_list(newLen, newItems)

    def _set_single_rebuild(self, index, value):
        self._set_slice(slice(index, index + 1, 1), [value])

    def _checkindex(self, index):
        length = len(self)
        if 0 <= index < length:
            return index
        if -length <= index < 0:
            return index + length
        raise IndexError("invalid index: %s" % index)

    def _check_allowed(self, items):
        if hasattr(self, "_allowed"):
            if False in [isinstance(val, self._allowed) for val in items]:
                raise TypeError("Invalid type encountered in the arguments.")

    def _set_slice(self, index, values):
        "Assign values to a slice of the object"
        try:
            valueList = list(values)
        except TypeError:
            raise TypeError("can only assign an iterable to a slice")

        self._check_allowed(valueList)

        origLen = len(self)
        start, stop, step = index.indices(origLen)

        
        
        if index.step is None:
            self._assign_simple_slice(start, stop, valueList)
        else:
            self._assign_extended_slice(start, stop, step, valueList)

    def _assign_extended_slice_rebuild(self, start, stop, step, valueList):
        "Assign an extended slice by rebuilding entire list"
        indexList = range(start, stop, step)
        
        if len(valueList) != len(indexList):
            raise ValueError(
                "attempt to assign sequence of size %d "
                "to extended slice of size %d" % (len(valueList), len(indexList))
            )

        
        newLen = len(self)
        newVals = dict(zip(indexList, valueList))

        def newItems():
            for i in range(newLen):
                if i in newVals:
                    yield newVals[i]
                else:
                    yield self._get_single_internal(i)

        self._rebuild(newLen, newItems())

    def _assign_extended_slice(self, start, stop, step, valueList):
        "Assign an extended slice by re-assigning individual items"
        indexList = range(start, stop, step)
        
        if len(valueList) != len(indexList):
            raise ValueError(
                "attempt to assign sequence of size %d "
                "to extended slice of size %d" % (len(valueList), len(indexList))
            )

        for i, val in zip(indexList, valueList):
            self._set_single(i, val)

    def _assign_simple_slice(self, start, stop, valueList):
        "Assign a simple slice; Can assign slice of any length"
        origLen = len(self)
        stop = max(start, stop)
        newLen = origLen - stop + start + len(valueList)

        def newItems():
            for i in range(origLen + 1):
                if i == start:
                    yield from valueList

                if i < origLen:
                    if i < start or i >= stop:
                        yield self._get_single_internal(i)

        self._rebuild(newLen, newItems())