You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mutable_list.py 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. # Copyright (c) 2008-2009 Aryeh Leib Taurog, all rights reserved.
  2. # Released under the New BSD license.
  3. """
  4. This module contains a base type which provides list-style mutations
  5. without specific data storage methods.
  6. See also http://static.aryehleib.com/oldsite/MutableLists.html
  7. Author: Aryeh Leib Taurog.
  8. """
  9. from functools import total_ordering
  10. @total_ordering
  11. class ListMixin:
  12. """
  13. A base class which provides complete list interface.
  14. Derived classes must call ListMixin's __init__() function
  15. and implement the following:
  16. function _get_single_external(self, i):
  17. Return single item with index i for general use.
  18. The index i will always satisfy 0 <= i < len(self).
  19. function _get_single_internal(self, i):
  20. Same as above, but for use within the class [Optional]
  21. Note that if _get_single_internal and _get_single_internal return
  22. different types of objects, _set_list must distinguish
  23. between the two and handle each appropriately.
  24. function _set_list(self, length, items):
  25. Recreate the entire object.
  26. NOTE: items may be a generator which calls _get_single_internal.
  27. Therefore, it is necessary to cache the values in a temporary:
  28. temp = list(items)
  29. before clobbering the original storage.
  30. function _set_single(self, i, value):
  31. Set the single item at index i to value [Optional]
  32. If left undefined, all mutations will result in rebuilding
  33. the object using _set_list.
  34. function __len__(self):
  35. Return the length
  36. int _minlength:
  37. The minimum legal length [Optional]
  38. int _maxlength:
  39. The maximum legal length [Optional]
  40. type or tuple _allowed:
  41. A type or tuple of allowed item types [Optional]
  42. """
  43. _minlength = 0
  44. _maxlength = None
  45. # ### Python initialization and special list interface methods ###
  46. def __init__(self, *args, **kwargs):
  47. if not hasattr(self, '_get_single_internal'):
  48. self._get_single_internal = self._get_single_external
  49. if not hasattr(self, '_set_single'):
  50. self._set_single = self._set_single_rebuild
  51. self._assign_extended_slice = self._assign_extended_slice_rebuild
  52. super().__init__(*args, **kwargs)
  53. def __getitem__(self, index):
  54. "Get the item(s) at the specified index/slice."
  55. if isinstance(index, slice):
  56. return [self._get_single_external(i) for i in range(*index.indices(len(self)))]
  57. else:
  58. index = self._checkindex(index)
  59. return self._get_single_external(index)
  60. def __delitem__(self, index):
  61. "Delete the item(s) at the specified index/slice."
  62. if not isinstance(index, (int, slice)):
  63. raise TypeError("%s is not a legal index" % index)
  64. # calculate new length and dimensions
  65. origLen = len(self)
  66. if isinstance(index, int):
  67. index = self._checkindex(index)
  68. indexRange = [index]
  69. else:
  70. indexRange = range(*index.indices(origLen))
  71. newLen = origLen - len(indexRange)
  72. newItems = (self._get_single_internal(i)
  73. for i in range(origLen)
  74. if i not in indexRange)
  75. self._rebuild(newLen, newItems)
  76. def __setitem__(self, index, val):
  77. "Set the item(s) at the specified index/slice."
  78. if isinstance(index, slice):
  79. self._set_slice(index, val)
  80. else:
  81. index = self._checkindex(index)
  82. self._check_allowed((val,))
  83. self._set_single(index, val)
  84. # ### Special methods for arithmetic operations ###
  85. def __add__(self, other):
  86. 'add another list-like object'
  87. return self.__class__([*self, *other])
  88. def __radd__(self, other):
  89. 'add to another list-like object'
  90. return other.__class__([*other, *self])
  91. def __iadd__(self, other):
  92. 'add another list-like object to self'
  93. self.extend(other)
  94. return self
  95. def __mul__(self, n):
  96. 'multiply'
  97. return self.__class__(list(self) * n)
  98. def __rmul__(self, n):
  99. 'multiply'
  100. return self.__class__(list(self) * n)
  101. def __imul__(self, n):
  102. 'multiply'
  103. if n <= 0:
  104. del self[:]
  105. else:
  106. cache = list(self)
  107. for i in range(n - 1):
  108. self.extend(cache)
  109. return self
  110. def __eq__(self, other):
  111. olen = len(other)
  112. for i in range(olen):
  113. try:
  114. c = self[i] == other[i]
  115. except IndexError:
  116. # self must be shorter
  117. return False
  118. if not c:
  119. return False
  120. return len(self) == olen
  121. def __lt__(self, other):
  122. olen = len(other)
  123. for i in range(olen):
  124. try:
  125. c = self[i] < other[i]
  126. except IndexError:
  127. # self must be shorter
  128. return True
  129. if c:
  130. return c
  131. elif other[i] < self[i]:
  132. return False
  133. return len(self) < olen
  134. # ### Public list interface Methods ###
  135. # ## Non-mutating ##
  136. def count(self, val):
  137. "Standard list count method"
  138. count = 0
  139. for i in self:
  140. if val == i:
  141. count += 1
  142. return count
  143. def index(self, val):
  144. "Standard list index method"
  145. for i in range(0, len(self)):
  146. if self[i] == val:
  147. return i
  148. raise ValueError('%s not found in object' % val)
  149. # ## Mutating ##
  150. def append(self, val):
  151. "Standard list append method"
  152. self[len(self):] = [val]
  153. def extend(self, vals):
  154. "Standard list extend method"
  155. self[len(self):] = vals
  156. def insert(self, index, val):
  157. "Standard list insert method"
  158. if not isinstance(index, int):
  159. raise TypeError("%s is not a legal index" % index)
  160. self[index:index] = [val]
  161. def pop(self, index=-1):
  162. "Standard list pop method"
  163. result = self[index]
  164. del self[index]
  165. return result
  166. def remove(self, val):
  167. "Standard list remove method"
  168. del self[self.index(val)]
  169. def reverse(self):
  170. "Standard list reverse method"
  171. self[:] = self[-1::-1]
  172. def sort(self, key=None, reverse=False):
  173. "Standard list sort method"
  174. self[:] = sorted(self, key=key, reverse=reverse)
  175. # ### Private routines ###
  176. def _rebuild(self, newLen, newItems):
  177. if newLen and newLen < self._minlength:
  178. raise ValueError('Must have at least %d items' % self._minlength)
  179. if self._maxlength is not None and newLen > self._maxlength:
  180. raise ValueError('Cannot have more than %d items' % self._maxlength)
  181. self._set_list(newLen, newItems)
  182. def _set_single_rebuild(self, index, value):
  183. self._set_slice(slice(index, index + 1, 1), [value])
  184. def _checkindex(self, index):
  185. length = len(self)
  186. if 0 <= index < length:
  187. return index
  188. if -length <= index < 0:
  189. return index + length
  190. raise IndexError('invalid index: %s' % index)
  191. def _check_allowed(self, items):
  192. if hasattr(self, '_allowed'):
  193. if False in [isinstance(val, self._allowed) for val in items]:
  194. raise TypeError('Invalid type encountered in the arguments.')
  195. def _set_slice(self, index, values):
  196. "Assign values to a slice of the object"
  197. try:
  198. valueList = list(values)
  199. except TypeError:
  200. raise TypeError('can only assign an iterable to a slice')
  201. self._check_allowed(valueList)
  202. origLen = len(self)
  203. start, stop, step = index.indices(origLen)
  204. # CAREFUL: index.step and step are not the same!
  205. # step will never be None
  206. if index.step is None:
  207. self._assign_simple_slice(start, stop, valueList)
  208. else:
  209. self._assign_extended_slice(start, stop, step, valueList)
  210. def _assign_extended_slice_rebuild(self, start, stop, step, valueList):
  211. 'Assign an extended slice by rebuilding entire list'
  212. indexList = range(start, stop, step)
  213. # extended slice, only allow assigning slice of same size
  214. if len(valueList) != len(indexList):
  215. raise ValueError('attempt to assign sequence of size %d '
  216. 'to extended slice of size %d'
  217. % (len(valueList), len(indexList)))
  218. # we're not changing the length of the sequence
  219. newLen = len(self)
  220. newVals = dict(zip(indexList, valueList))
  221. def newItems():
  222. for i in range(newLen):
  223. if i in newVals:
  224. yield newVals[i]
  225. else:
  226. yield self._get_single_internal(i)
  227. self._rebuild(newLen, newItems())
  228. def _assign_extended_slice(self, start, stop, step, valueList):
  229. 'Assign an extended slice by re-assigning individual items'
  230. indexList = range(start, stop, step)
  231. # extended slice, only allow assigning slice of same size
  232. if len(valueList) != len(indexList):
  233. raise ValueError('attempt to assign sequence of size %d '
  234. 'to extended slice of size %d'
  235. % (len(valueList), len(indexList)))
  236. for i, val in zip(indexList, valueList):
  237. self._set_single(i, val)
  238. def _assign_simple_slice(self, start, stop, valueList):
  239. 'Assign a simple slice; Can assign slice of any length'
  240. origLen = len(self)
  241. stop = max(start, stop)
  242. newLen = origLen - stop + start + len(valueList)
  243. def newItems():
  244. for i in range(origLen + 1):
  245. if i == start:
  246. yield from valueList
  247. if i < origLen:
  248. if i < start or i >= stop:
  249. yield self._get_single_internal(i)
  250. self._rebuild(newLen, newItems())