How do I prevent my list comprehension from raising a stopIteration error

694 Views Asked by At

I'm stepping through a text file with records that are 3-lines long. If, in the first line, I can say "this is a record that I don't want to count", I want to move to the start of the next record, 2 lines further down. Currently, I'm running a code snippet like this:

lines = content.split("\n")
iterable = iter(xrange(len(lines)))
for i in iterable:
    line = lines[i]
...
    if isRecord(keyword) == False:
        [iterable.next() for x in range(2)]

At the very end of the file, there's a chance that my comprehension'll kick out a stopIteration error. How do I add to my code so that if I raise a stopIteration, that it'll break the for loop? I've looked at a number of entries on list comprehensions, along with how the for-loops are built to stop based on the stopIteration flag, but I don't yet understand how to apply that to my own code. I've also seen list comprehensions with an if/else/for style, but can I build one that has a style like:

[iterable.next() for x in range(2) else break]

Thanks for all of the help, sincerely.

2

There are 2 best solutions below

2
On BEST ANSWER

You can take a slice using itertools. some_list will be length 2, 1 or 0, depending on how much of the list remains. If the list is larger, 2 items will be removed from the iterator and the for loop will continue with the next item.

import itertools

lines = content.split("\n")
iterable = iter(xrange(len(lines)))
for i in iterable:
    line = lines[i]
...
    if isRecord(keyword) == False:
        some_list = list(itertools.islice(iterable, 2))
1
On

you can also construct your iterable in such a way that you get 3 items at the time, for example with this recipe from the itertools module

from itertools import izip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

for example

>>> for x in grouper(xrange(10),3):
        print x


(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
(9, None, None)
>>> 

so for you case you can do this

lines = content.split("\n")
for line,x,y in grouper(lines,3):
    ...
    if not isRecord(keyword) :
        continue # go to the next iteration

or if the content is not format in a pure 3 lines block, then the consume recipe

from itertools import islice
import collections 

def consume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

example

>>> it=iter(xrange(10))
>>> consume(it,5)
>>> list(it)
[5, 6, 7, 8, 9]
>>> 

you can also use enumerate to know were you are if you really need that, like for example

lines = content.split("\n")
iterator = iter(enumerate(lines))
for i,line in iterator:
    ...
    if not isRecord(keyword) :
        consume(iterator,2)