Package lastfm :: Module lazylist

[hide private]
[frames] | no frames]

# Source Code for Module lastfm.lazylist

      1  """Module for the creation and use of iterator-based lazy lists.
      2  this module defines a class LazyList which can be used to represent sequences
      3  of values generated lazily. One can also create recursively defined lazy lists
      4  that generate their values based on ones previously generated.
      5
      6  Backport to python 2.5 by Michael Pust
      7  """
      8
      9  __author__ = 'Dan Spitz'
     10  __all__ = ('LazyList', 'RecursiveLazyList', 'lazylist')
     11
     12  import itertools
     13
     14 -class LazyList(object):
     15      """A Sequence whose values are computed lazily by an iterator.
     16      """
     17 -    def __init__(self, iterable):
     18          self._exhausted = False
     19          self._iterator = iter(iterable)
     20          self._data = []
     21
     22 -    def __len__(self):
     23          """Get the length of a LazyList's computed data."""
     24          return len(self._data)
     25
     26 -    def __getitem__(self, i):
     27          """Get an item from a LazyList.
     28          i should be a positive integer or a slice object."""
     29          if isinstance(i, int):
     30              #index has not yet been yielded by iterator (or iterator exhausted
     31              #before reaching that index)
     32              if i >= len(self):
     33                  self.exhaust(i)
     34              elif i < 0:
     35                  raise ValueError('cannot index LazyList with negative number')
     36              return self._data[i]
     37
     38          #LazyList slices are iterators over a portion of the list.
     39          elif isinstance(i, slice):
     40              start, stop, step = i.start, i.stop, i.step
     41              if any(x is not None and x < 0 for x in (start, stop, step)):
     42                  raise ValueError('cannot index or step through a LazyList with'
     43                                   'a negative number')
     44              #set start and step to their integer defaults if they are None.
     45              if start is None:
     46                  start = 0
     47              if step is None:
     48                  step = 1
     49
     50              def LazyListIterator():
     51                  count = start
     52                  predicate = (stop is None) and (lambda: True) or (lambda: count < stop)
     53                  while predicate():
     54                      try:
     55                          yield self[count]
     56                      #slices can go out of actual index range without raising an
     57                      #error
     58                      except IndexError:
     59                          break
     60                      count += step
     61              return LazyListIterator()
     62
     63          raise TypeError('i must be an integer or slice')
     64
     65 -    def __iter__(self):
     66          """return an iterator over each value in the sequence,
     67          whether it has been computed yet or not."""
     68          return self[:]
     69
     70 -    def computed(self):
     71          """Return an iterator over the values in a LazyList that have
     72          already been computed."""
     73          return self[:len(self)]
     74
     75 -    def exhaust(self, index = None):
     76          """Exhaust the iterator generating this LazyList's values.
     77          if index is None, this will exhaust the iterator completely.
     78          Otherwise, it will iterate over the iterator until either the list
     79          has a value for index or the iterator is exhausted.
     80          """
     81          if self._exhausted:
     82              return
     83          if index is None:
     84              ind_range = itertools.count(len(self))
     85          else:
     86              ind_range = range(len(self), index + 1)
     87
     88          for ind in ind_range:
     89              try:
     90                  self._data.append(self._iterator.next())
     91              except StopIteration: #iterator is fully exhausted
     92                  self._exhausted = True
     93                  break
     94
     95 -class RecursiveLazyList(LazyList):
     96 -    def __init__(self, prod, *args, **kwds):
     97          super(RecursiveLazyList,self).__init__(prod(self,*args, **kwds))
     98
     99 -    def __repr__(self):
    100          return "<lastfm.lazylist>"
    101
    102 -class RecursiveLazyListFactory:
    103 -    def __init__(self, producer):
    104          self._gen = producer
    105 -    def __call__(self,*a,**kw):
    106          return RecursiveLazyList(self._gen,*a,**kw)
    107
    108
    109 -def lazylist(gen):
    110      """Decorator for creating a RecursiveLazyList subclass.
    111      This should decorate a generator function taking the LazyList object as its
    112      first argument which yields the contents of the list in order.
    113      """
    114      return RecursiveLazyListFactory(gen)
    115
    116  #two examples
    117  if __name__ == '__main__':
    118      #fibonnacci sequence in a lazy list.
    119      @lazylist
    120 -    def fibgen(lst):
    121          yield 0
    122          yield 1
    123          for a, b in itertools.izip(lst, lst[1:]):
    124              yield a + b
    125
    126      fibs = fibgen() #now fibs can be indexed or iterated over as if it were
    127                      #an infinitely long list containing the fibonnaci sequence
    128
    129      #prime numbers in a lazy list.
    130      @lazylist
    131 -    def primegen(lst):
    132          yield 2
    133          for candidate in itertools.count(3): #start at next number after 2
    134              #if candidate is not divisible by any smaller prime numbers,
    135              #it is a prime.
    136              if all(candidate % p for p in lst.computed()):
    137                  yield candidate
    138      primes = primegen() #same for primes- treat it like an infinitely long list
    139                          #containing all prime numbers.
    140      print fibs[0], fibs[1], fibs[2], primes[0], primes[1], primes[2]
    141      print list(fibs[:10]), list(primes[:10])
    142