[Python-ideas] Add lookahead iterator (peeker) to itertools
Terry Reedy
tjreedy at udel.edu
Mon Feb 25 04:41:48 CET 2013
An iterator iter represents the remainder of some collection, concrete
or not, finite or not. If the remainder is not empty, its .__next__
method selects one object, mutates iter to represent the reduced
remainder (now possibly empty), and returns the one item.
At various times, people have asked for the ability to determine whether
an iterator is exhausted, whether a next() call will return or raise. If
it will return, people have also asked for the ability to peek at what
the return would be, without disturbing what the return will be. For
instance, on the 'iterable.__unpack__ method' Alex Stewart today wrote:
> The problem is that there is no standard way to query an iterator
> to find out if it has more data available without automatically
> consuming that next data element in the rocess.
It turns out that there is a solution that gives the ability to both
test emptiness (in the standard way) and peek ahead, without modifying
the iterator protocol. It merely require a wrapper iterator, much like
the ones in itertools. I have posted one before and give my current
version below.
Does anyone else this should be added to itertools? It seems to not be
completely obvious to everyone, is more complex that some of the
existing itertools, and cannot be composed from them either. (Nor can it
be written as a generator function.)
Any of the names can be changed. Perhaps the class should be 'peek' and
the lookahead object something else. The sentinel should be read-only if
possible. I considered whether the peek object should be read-only, but
someone would say that they *want* be able to replace the next object to
be yielded. Peeking into an exhausted iterable could raise instead of
returning the sentinel, but I don't know if that would be more useful.
----------------
class lookahead():
"Wrap iterator with lookahead to both peek and test exhausted"
_NONE = object()
def __init__(self, iterable):
self._it = iter(iterable)
self._set_peek()
def __iter__(self):
return self
def __next__(self):
ret = self.peek
self._set_peek()
return ret
def _set_peek(self):
try:
self.peek = next(self._it)
except StopIteration:
self.peek = self._NONE
def __bool__(self):
return self.peek is not self._NONE
def test_lookahead():
it = lookahead('abc')
while it:
a = it.peek
b = next(it)
print('next:', b, '; is peek:', a is b )
test_lookahead()
--------------------
>>>
next: a ; is peek: True
next: b ; is peek: True
next: c ; is peek: True
--
Terry Jan Reedy
More information about the Python-ideas
mailing list