40

In many languages we can do something like:

for (int i = 0; i < value; i++)
{
    if (condition)
    {
        i += 10;
    }
}

How can I do the same in Python? The following (of course) does not work:

for i in xrange(value):
    if condition:
        i += 10

I could do something like this:

i = 0
while i < value:
  if condition:
    i += 10
  i += 1

but I'm wondering if there is a more elegant (pythonic?) way of doing this in Python.

9 Answers 9

30

Use continue.

for i in xrange(value):
    if condition:
        continue

If you want to force your iterable to skip forwards, you must call .next().

>>> iterable = iter(xrange(100))
>>> for i in iterable:
...     if i % 10 == 0:
...         [iterable.next() for x in range(10)]
... 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]

As you can see, this is disgusting.

Sign up to request clarification or add additional context in comments.

9 Comments

Please, check my last edit. I changed it to skip 10 values of i instead of only one.
It depends on what your trying to do, what are you looping over, and why are you using i as the counter? The more pythonic way may be not to use the "counter" at all. It's hard to say without a more detailed example.
Iterators don't allow you to 'jump ahead' by an arbitrary number of steps. The only way to skip ahead is to call .next() on your iterator the number of times you want to skip.
@brad.ayers can you post an example of how to call .next() in the code you posted?
@brad.ayers, itertools.islice does the jump ahead on an iterator. Spend some time looking at itertools, you will fall in love. islice handles the next() calls for you.
|
24

Create the iterable before the loop.

Skip one by using next on the iterator

it = iter(xrange(value))
for i in it:
    if condition:
        i = next(it)

Skip many by using itertools or recipes based on ideas from itertools.

itertools.dropwhile()

it = iter(xrange(value))
for i in it:
    if x<5:
        i = dropwhile(lambda x: x<5, it)

Take a read through the itertools page, it shows some very common uses of working with iterators.

itertools islice

it = islice(xrange(value), 10)
for i in it:
    ...do stuff with i...

3 Comments

the first code returns the following exception: TypeError: xrange object is not an iterator. I think it will require to specify: it = xrange(value).__iter__()
@oscar, thx. I was just going off the top of my head. Just wrap it in an iter() call to instantiate an iterator.
@oscar, takewhile was not quite what you may have been looking for, I changed it to dropwhile. If you simply want skip over a bunch of an iterator, islice can do that quite affectively if it is a qtantity.
6

It's a very old question, but I find the accepted answer is not totally stisfactory:

  • first, after the if ... / [next()...] sequence, the value of i hasn't changed. In your first example, it has.
  • second, the list comprehension is used to produce a side-effect. This should be avoided.
  • third, there might be a faster way to achieve this.

Using a modified version of consume in itertools recipes, you can write:

import itertools

def consume(it, n):
    return next(itertools.islice(it, n-1, n), None)

it = iter(range(20))
for i in it:
    print(i, end='->')
    if i%4 == 0:
        i = consume(it, 5)
    print(i)

As written in the doctstring of consume, the iterator is consumed at C speed (didn't benchmark though). Output:

0->5
6->6
7->7
8->13
14->14
15->15
16->None

With a minor modification, one can get 21 instead of None, but I think this isnot a good idea because this code does work with any iterable (otherwise one would prefer the while version):

import string
it = iter(string.ascii_lowercase) # a-z
for x in it:
    print(x, end="->")
    if x in set('aeiouy'):
        x = consume(it, 2) # skip the two letters after the vowel
    print(x)

Output:

a->c
d->d
e->g
h->h
i->k
l->l
m->m
n->n
o->q
r->r
s->s
t->t
u->w
x->x
y->None

3 Comments

it should be n not in n-1 in consume()
@SmartManoj I just tested it with : >>> it = iter(range(10)) / >>> list(it) / [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] / >>> it = iter(range(10)) / >>> consume(it, 4) / 3 / >>> list(it) / [4, 5, 6, 7, 8, 9]. Seems ok to me.
if it is n, consume returns None else the last consumed element
3

Itertools has a recommended way to do this: https://docs.python.org/3.7/library/itertools.html#itertools-recipes

import collections
def tail(n, iterable):
    "Return an iterator over the last n items"
    # tail(3, 'ABCDEFG') --> E F G
    return iter(collections.deque(iterable, maxlen=n))

Now you can do:

for i in tail(5, range(10)):
    print(i)

to get

5
6
7
8
9

3 Comments

This does kind of the opposite of the request. The request is to "skip N items", but this answer shows how to skip all but N items. Obv this isn't too difficult to account for if the total number of items is known ahead-of-time, but that isn't always known. It also wastefully forces all items to be consumed immediately. (It isn't wasteful if you actually want tail, but it's wasteful compared to what we really want, which is isplice(iter, N, None).)
@tho What about the consume function in that recipe?
@josh isplice from?
2

Easiest way is to use more_itertools.consume(). See https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume.

import more_itertools

x = iter(range(10))

# skip the next four items
more_itertools.consume(x, 4)
y = list(x)
print(y)

will result in

[4, 5, 6, 7, 8, 9]

This becomes super helpful if you are iterating through a file and need to skip a bunch of lines. Something like this:

with open("test.txt", "r") as file:
    while line:= next(file):

        # Look for "magic_word" and then skip 10 lines.
        if "magic_word" in line:
            more_itertools.consume(file, 10)
    
        # do other stuff with line

1 Comment

I think this is prolly the best soln considering its an inbuilt python lib based solution
1

I am hoping I am not answering this wrong... but this is the simplest way I have come across:

for x in range(0,10,2):
    print x

output should be something like this:

0
2
4
6
8

The 2 in the range parameter's is the jump value

Comments

0

Does a generator function here is rebundant? Like this:

def filterRange(range, condition):
x = 0
while x < range:
    x = (x+10) if condition(x) else (x + 1)
    yield x

if __name__ == "__main__":
for i in filterRange(100, lambda x: x > 2):
    print i

Comments

0

There are a few ways to create iterators, but the custom iterator class is the most extensible:

class skip_if:   # skip_if(object) for python2
    """
    iterates through iterable, calling skipper with each value
    if skipper returns a positive integer, that many values are
    skipped
    """
    def __init__(self, iterable, skipper):
        self.it = iter(iterable)
        self.skip = skipper
    def __iter__(self):
        return self
    def __next__(self):   # def next(self): for python2
        value = next(self.it)
        for _ in range(self.skip(value)):
            next(self.it, None)
        return value

and in use:

>>> for i in skip_if(range(1,100), lambda n: 10 if not n%10 else 0):
...   print(i, end=', ')
... 
 1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,

Comments

-1

I think you have to use a while loop for this...for loop loops over an iterable..and you cannot skip next item like how you want to do it here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.