123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- # Copyright (c) 2008-2009 Aryeh Leib Taurog, all rights reserved.
- # 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
-
- Author: Aryeh Leib Taurog.
- """
- from functools import total_ordering
-
-
- @total_ordering
- class ListMixin:
- """
- 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]
- """
-
- _minlength = 0
- _maxlength = None
-
- # ### Python initialization and special list interface methods ###
-
- 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)
-
- # calculate new length and dimensions
- 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)
-
- # ### Special methods for arithmetic operations ###
- 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:
- # self must be shorter
- 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:
- # self must be shorter
- return True
- if c:
- return c
- elif other[i] < self[i]:
- return False
- return len(self) < olen
-
- # ### Public list interface Methods ###
- # ## Non-mutating ##
- 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)
-
- # ## Mutating ##
- 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)
-
- # ### Private routines ###
- 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)
-
- # CAREFUL: index.step and step are not the same!
- # step will never be None
- 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)
- # extended slice, only allow assigning slice of same size
- if len(valueList) != len(indexList):
- raise ValueError('attempt to assign sequence of size %d '
- 'to extended slice of size %d'
- % (len(valueList), len(indexList)))
-
- # we're not changing the length of the sequence
- 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)
- # extended slice, only allow assigning slice of same size
- 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())
|