Source code for pyquchk.lazylist

from itertools import count


[docs]class LazyList(object): """A Sequence whose values are computed lazily by an iterator. .. ipython:: >>> it = count() >>> ll = LazyList(it) >>> list(ll[:10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(ll[:10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(ll[:10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> ll[123] 123 >>> list(ll[1:10:2]) [1, 3, 5, 7, 9] """ def __init__(self, iterable): self._exhausted = False self._iterator = iter(iterable) self._data = []
[docs] def __len__(self): """Get the length of a LazyList's computed data.""" return len(self._data)
[docs] def __getitem__(self, i): """Get an item from a LazyList. i should be a positive integer or a slice object.""" if isinstance(i, int): #index has not yet been yielded by iterator (or iterator exhausted #before reaching that index) if i >= len(self): self.exhaust(i) elif i < 0: raise ValueError('cannot index LazyList with negative number') return self._data[i] #LazyList slices are iterators over a portion of the list. elif isinstance(i, slice): start, stop, step = i.start, i.stop, i.step if any(x is not None and x < 0 for x in (start, stop, step)): raise ValueError('cannot index or step through a LazyList with' 'a negative number') #set start and step to their integer defaults if they are None. if start is None: start = 0 if step is None: step = 1 def LazyListIterator(): count = start while stop is None or count < stop: try: yield self[count] #slices can go out of actual index range without raising an #error except IndexError: break count += step return LazyListIterator() raise TypeError('i must be an integer or slice')
[docs] def __iter__(self): """Return an iterator over each value in the sequence, whether it has been computed yet or not.""" return self[:]
[docs] def computed(self): """Return an iterator over the values in a LazyList that have already been computed.""" return self[:len(self)]
[docs] def exhaust(self, index=None): """Exhaust the iterator generating this LazyList's values. if index is None, this will exhaust the iterator completely. Otherwise, it will iterate over the iterator until either the list has a value for index or the iterator is exhausted. """ if self._exhausted: return if index is None: ind_range = count(len(self)) else: ind_range = range(len(self), index + 1) for ind in ind_range: try: self._data.append(next(self._iterator)) except StopIteration: #iterator is fully exhausted self._exhausted = True break