views:

135

answers:

3

I have a program as follows:

a=reader.next()
if *some condition holds*:
    #Do some processing and continue the iteration
else:
    #Append the variable a back to the iterator
    #That is nullify the operation *a=reader.next()*

How do I add an element to the start of the iterator? (Or is there an easier way to do this?)

EDIT: OK let me put it this way. I need the next element in an iterator without removing it. How do I do this>?

+4  A: 

Python iterators, as such, have very limited functionality -- no "appending" or anything like that. You'll need to wrap the generic iterator in a wrapper adding that functionality. E.g.:

class Wrapper(object):
  def __init__(self, it):
    self.it = it
    self.pushedback = []
  def __iter__(self):
    return self
  def next(self):
    if self.pushedback:
      return self.pushedback.pop()
    else:
      return self.it.next()
  def pushback(self, val):
    self.pushedback.append(val)

This is Python 2.5 (should work in 2.6 too) -- slight variants advised for 2.6 and mandatory for 3.any (use next(self.it) instead of self.it.next() and define __next__ instead of next).

Edit: the OP now says what they need is "peek ahead without consuming". Wrapping is still the best option, but an alternative is:

import itertools
   ...
o, peek = itertools.tee(o)
if isneat(peek.next()): ...

this doesn't advance o (remember to advance it if and when you decide you DO want to;-).

Alex Martelli
Yes.This will work.Only problem is the iterator is quite large but if there is no other way this will have to do for now
Goutham
No worry -- the iterator is held by reference in the wrapper case, and itertools.tee will consume extra space O(K) where K is the number of items you need to peek ahead. So "large iterators" are no problem!
Alex Martelli
+1  A: 

By design (in general development concepts) iterators are intended to be read-only, and any attempt to change them would break.

Alternatively, you could read the iterator backwards, and add it to the end of hte element (which is actually the start :) )?

Russell
A: 

This isn't too close what you asked for, but if you have control over the generator and you don't need to "peek" before the value is generated (and any side effects have occurred), you can use the generator.send method to tell the generator to repeat the last value it yielded:

>>> def a():
...     for x in (1,2,3):
...             rcvd = yield x
...             if rcvd is not None:
...                     yield x
... 
>>> gen = a()
>>> gen.send("just checking")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> gen.next()
1
>>> gen.send("just checking")
1
>>> gen.next()
2
>>> gen.next()
3
>>> gen.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Mark Rushakoff