views:

421

answers:

8

Hello.

It's a common programming task to loop iteration while not receiving next item. For example:

for sLine in oFile :
  if ... some logic ... :
    sLine = oFile.next()
    ... some more logic ...
    # at this point i want to continue iteration but without
    # getting next item from oFile. How can this be done in python?
+3  A: 

I first thought you wanted the continue keyword, but that would of course get you the next line of input.

I think I'm stumped. When looping over the lines of a file, what exactly should happen if you continued the loop without getting a new line?

Do you want to inspect the line again? If so, I suggest adding an inner loop that runs until you're "done" with the input line, which you can then break out of, or use maybe the while-condition and a flag variable to terminate.

unwind
+1: I was rather stumped too by how someone could expect a file iterator to go on to the next iteration without reading the next line :-)
Jarret Hardie
A: 

Instead of looping through the file line by line, you can do a file.readlines() into a list. Then loop through the list (which allows you to look at the next item if you want). Example:

list = oFile.readlines()
for i in range(len(list))
    #your stuff
    list[i] #current line
    list[i-1] #previous line
    list[i+1] #next line

you can go to the previous or next line by simply using [i-1] or [i+1]. You just need to make sure that no matter what, i does not go out of range.

jle
is it a way to move one element backwards while iterating list via for in in list ? In text parsing it's a common task if you accumulate lines until you find a line that NOT fits and you need to check this line again
Eye of Hell
@eye of hell: if you have a particular use case in mind, please update the question with the description of what you are trying to accomplish in that use case.
Jarret Hardie
note that even if you use this method i will still be reset to the next number, so you'd also need to remove the next entry from the list (which is expensive).
James Antill
+1  A: 

What you need is a simple, deterministic finite state machine. Something like this...

state = 1
for sLine in oFile:
   if state == 1:
      if ... some logic ... :
         state = 2
   elif state == 2:
      if ... some logic ... :
         state = 1
Glenn
no other ways in python? state machine is a good choice, but needs a bit more code :(
Eye of Hell
I may be missing something. How does this help the user put an object back on the front of his iterable for re-consumption?
Brandon Craig Rhodes
Putting an object back isn't the problem. Putting an object back is a solution to a problem. It's just not a good solution. If it was, then programming languages would provide a way to handle it easily. From a CE perspective, FSM is a better solution.
Glenn
A: 

jle's approach should work, though you might as well use enumerate():

for linenr, line in enumerate(oFile):
    # your stuff
lt_kije
A: 

Ugly. Wrap the body in another loop.

for sLine in oFile :
  while 1:
    if ... some logic ... :
      sLine = oFile.next()
      ... some more logic ...
      continue
    ... some more logic ...
    break

A little better:

class pushback_iter(object):
    def __init__(self,iterable):
        self.iterator = iter(iterable)
        self.buffer = []
    def next(self):
        if self.buffer:
            return self.buffer.pop()
        else:
            return self.iterator.next()
    def __iter__(self):
        return self
    def push(self,item):
        self.buffer.append(item)

it_file = pushback_iter(file)
for sLine in it_file:
    if ... some logic ... :
        sLine = it_file.next()
        ... some more logic ...
        it_file.push(sLine)
        continue
    ... some more logic ...

Unfortunately no simple way of referencing the current iterator.

MizardX
This looks like a decent solution — not as compact as the suggestion I made about using a generator, but, still, something that uses the same logic, and one of the very few answers here that actually solves the questioner's problem.
Brandon Craig Rhodes
A: 

You can assign your iterator to an variable then use the .next get te next one.

iter = oFile.xreadlines() # is this the correct iterator you want?
try:
  sLine = iter.next()
  while True:
    if ... some logic ... :
      sLine = iter.next()
      ... some more logic ...
      continue
    sLine = iter.next()
except StopIterator:
  pass
Stefan Lundström
readlines() isn't an iterator. It returns a list, and lists don't have a 'next()' property.
Jarret Hardie
lets use xreadlines then.
Stefan Lundström
i missread the question, he wants to peak it
Stefan Lundström
+1  A: 

Simply create an iterator of your own that lets you push data back on the front of the stream so that you can give the loop a line that you want to see over again:

next_lines = []
def prependIterator(i):
    while True:
        if next_lines:
            yield(next_lines.pop())
        else:
            yield(i.next())

for sLine in prependIterator(oFile):
    if ... some logic ... :
        sLine = oFile.next()
        ... some more logic ...
        # put the line back so that it gets read
        # again as we head back up to the "for
        # statement
        next_lines.append(sLine)

If the prepend_list is never touched, then the prependIterator behaves exactly like whatever iterator it is passed: the if statement inside will always get False and it will just yield up everything in the iterator it has been passed. But if items are placed on the prepend_list at any point during the iteration, then those will be yielded first instead before it returns back to reading from the main iterator.

Brandon Craig Rhodes
A: 

You just need to create a variable out of your iterator, and then manipulate that:

% cat /tmp/alt-lines.py 
#! /usr/bin/python -tt

import sys

lines = iter(open(sys.argv[1]))
for line in lines:
    print line,
    lines.next()

% cat /tmp/test
1
2
3
4
% /tmp/alt-lines.py /tmp/test
1
3

...note that in this case we unconditionally do lines.next() so the above fails for files with odd lines, but I assume that isn't going to be the case for you (and adding the error checking is fairly trivial -- just catch and throw away StopIteration on the manual .next()).

James Antill