Python Cookbook 19.18: 可前瞻的迭代器

  1 ## {{{ http://code.activestate.com/recipes/577361/ (r1)
  2 import collections
  3 class peekable(object):
  4     """ An iterator that supports a peek operation. 
  5     
  6     this is a merge of example 19.18 of python cookbook part 2, peek ahead more steps
  7     and the simpler example 16.7, which peeks ahead one step and stores it in
  8     the self.preview variable.
  9     
 10     Adapted so the peek function never raises an error, but gives the
 11     self.sentinel value in order to identify the exhaustion of the iter object.
 12     
 13     Example usage:
 14     
 15     >>> p = peekable(range(4))
 16     >>> p.peek()
 17     0
 18     >>> p.next(1)
 19     [0]
 20     >>> p.isFirst()
 21     True
 22     >>> p.preview
 23     1
 24     >>> p.isFirst()
 25     True
 26     >>> p.peek(3)
 27     [1, 2, 3]
 28     >>> p.next(2)
 29     [1, 2]
 30     >>> p.peek(2) #doctest: +ELLIPSIS
 31     [3, <object object at ...>]
 32     >>> p.peek(1)
 33     [3]
 34     >>> p.next(2)
 35     Traceback (most recent call last):
 36     StopIteration
 37     >>> p.next()
 38     3
 39     >>> p.isLast()
 40     True
 41     >>> p.next()
 42     Traceback (most recent call last):
 43     StopIteration
 44     >>> p.next(0)
 45     []
 46     >>> p.peek()  #doctest: +ELLIPSIS
 47     <object object at ...>
 48     >>> p.preview #doctest: +ELLIPSIS
 49     <object object at ...>
 50     >>> p.isLast()  # after the iter process p.isLast remains True
 51     True
 52     """
 53     sentinel = object() #schildwacht
 54     def __init__(self, iterable):
 55         self._nit = iter(iterable).next  # for speed
 56         self._iterable = iter(iterable)
 57         self._cache = collections.deque()
 58         self._fillcache(1)          # initialize the first preview already
 59         self.preview = self._cache[0]
 60         self.count = -1  # keeping the count, possible to check
 61                          # isFirst and isLast status
 62     def __iter__(self):
 63         return self
 64     def _fillcache(self, n):
 65         """fill _cache of items to come, with one extra for the preview variable
 66         """
 67         if n is None:
 68             n = 1
 69         while len(self._cache) < n+1:
 70             try:
 71                 Next = self._nit()
 72             except StopIteration:
 73                 # store sentinel, to identify end of iter:
 74                 Next = self.sentinel
 75             self._cache.append(Next)
 76     def next(self, n=None):
 77         """gives next item of the iter, or a list of n items
 78         
 79         raises StopIteration if the iter is exhausted (self.sentinel is found),
 80         but in case of n > 1 keeps the iter alive for a smaller "next" calls
 81         """
 82         self._fillcache(n)
 83         if n is None:
 84             result = self._cache.popleft()
 85             if result == self.sentinel:
 86                 # find sentinel, so end of iter:
 87                 self.preview = self._cache[0]
 88                 raise StopIteration
 89             self.count += 1
 90         else:
 91             result = [self._cache.popleft() for i in range(n)]
 92             if result and result[-1] == self.sentinel:
 93                 # recache for future use:
 94                 self._cache.clear()
 95                 self._cache.extend(result)
 96                 self.preview = self._cache[0]
 97                 raise StopIteration
 98             self.count += n
 99         self.preview = self._cache[0]
100         return result
101     
102     def isFirst(self):
103         """returns true if iter is at first position
104         """
105         return self.count == 0
106 
107     def isLast(self):
108         """returns true if iter is at last position or after StopIteration
109         """
110         return self.preview == self.sentinel
111         
112     def peek(self, n=None):
113         """gives next item, without exhausting the iter, or a list of 0 or more next items
114         
115         with n == None, you can also use the self.preview variable, which is the first item
116         to come.
117         """
118         self._fillcache(n)
119         if n is None:
120             result = self._cache[0]
121         else:
122             result = [self._cache[i] for i in range(n)]
123         return result
124 ## end of http://code.activestate.com/recipes/577361/ }}}

 

posted on 2012-12-30 16:38  rongyilin  阅读(213)  评论(0编辑  收藏  举报

导航