How do I skip a few iterations in a for loop - python

In python I usually loop through ranges simply by
for i in range(100):
#do something
but now I want to skip a few steps in the loop. More specifically, I want something like continue(10) so that it would skip the whole loop and increase the counter by 10. If I were using a for loop in C I'd just sum 10 to i, but in Python that doesn't really work.

You cannot alter the target list (i in this case) of a for loop. Use a while loop instead:
while i < 10:
i += 1
if i == 2:
i += 3
Alternatively, use an iterable and increment that:
from itertools import islice
numbers = iter(range(10))
for i in numbers:
if i == 2:
next(islice(numbers, 3, 3), None) # consume 3
By assigning the result of iter() to a local variable, we can advance the loop sequence inside the loop using standard iteration tools (next(), or here, a shortened version of the itertools consume recipe). for normally calls iter() for us when looping over a iterator.

The best way is to assign the iterator a name - it is common have an iterable as opposed to an iterator (the difference being an iterable - for example a list - starts from the beginning each time you iterate over it). In this case, just use the iter() built-in function:
numbers = iter(range(100))
Then you can advance it inside the loop using the name. The best way to do this is with the itertools consume() recipe - as it is fast (it uses itertools functions to ensure the iteration happens in low-level code, making the process of consuming the values very fast, and avoids using up memory by storing the consumed values):
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)
By doing this, you can do something like:
numbers = iter(range(100))
for i in numbers:
...
if some_check(i):
consume(numbers, 3) # Skip 3 ahead.

Why not just set the value to skip until? Like:
skip_until = 0
for i in range(100):
if i < skip_until:
continue
if SOME_CONDITION:
skip_until = i + 10
DO_SOMETHING()
where SOME_CONDITION is whatever causes you to skip and DO_SOMETHING() is the actual loop contents?

for i in range(0, 100, 10):
print(i)
will print 0, 10, 20 ...

Something that I keep using. You can use below to get a 10% prob on running your code:
import random
for i in range(1000000):
if random.random() > 0.1:
continue:
# your code

A charmed and simplest form is like that:
>>> for i in range(5,10):
... print (i)
...
5
6
7
8
9
Where 5 was the index that started iteration.

Related

How to force a for loop counter to skip iterations in Python3?

I recently ran into an issue where I was using a for loop somewhat similar to this:
for i in range(lineCount(fileToBeProcessed)):
print(i)
j = doSomeStuff() #returns number of lines in the file to skip
i = i+j
print(i)
print('next_loop')
For a value of j={2,3,1} the output was:
1
3
next_loop
2
5
next_loop
.
.
My desired output:
1
3
next_loop
4
7
next_loop
.
.
Every time the next iteration started, the for loop counter i reset to the original cycle. My question is, is there a way to force the for loop to skip the iterations based on the return value j. I understand and was able to implement something similar with a while loop. However, I was curious as to how or why would Python not allow such manipulation?
It allows manipulations. But a for loop in Python works with a:
for <var> in <iterable>:
# ...
So Python does not attaches a special meaning to range(n) as a for loop: a range(n) is an iterable that iterates from 0 to n (exclusive). At the end of each iteration the next element of the iterable. It furthermore means that once you constructed a range(n), if you alter n, it has no impact on the for loop. This in contrast with for instance Java, where n is evaluated each iteration again.
Therefore you can manipulate the variable, but after the end of the loop, it will be assigned the next value of the loop.
In order to manipulate the variable, you can use a while loop:
i = 0 # initialization
while i < lineCount(fileToBeProcessed): # while loop
print(i)
j = doSomeStuff() #returns number of lines in the file to skip
i = i+j
print(i)
print('next_loop')
i += 1 # increment of the for loop is explicit here
Usually a while loop is considered to be "less safe" since you have to do the increment yourself (for all code paths in the loop). Since it is something one tends to forget, it is easier to write an endless loop.
Assuming that fileToBeProcessed is actually a file-like object, you can iterate directly over the file (i.e. over the lines in that file), or use enumerate(fileToBeProcessed) if you need the line numbers, and call next on that iterator to skip lines.
Like this (not tested):
iterator = enumerate(fileToBeProcessed) # or just iter = fileToBeProcessed
for i, line in iterator:
print(i)
j = doSomeStuff() #returns number of lines in the file to skip
for _ in range(j):
i, line = next(iterator) # advance iterator -> skip lines
print(i)
print('next_loop')
I've edited the code hope it may help
z =0
for i in range(lineCount(fileToBeProcessed)):
if i <= z: #if i is a value that you don't want to be output, then skip loop to next one
continue
print(i)
j = doSomeStuff()
cnt += 1
z = i+j #use a different variable from i since i the iterator value will not be updated
print(z)
print('next_loop')

A single python loop from zero to n to zero

Is there a Pythonesque way to create a loop that traverses a range from 0 to n and then back to 0?
I could just create 2 loops (one forward and one reverse), but I'm looking for single loop.
My goal is to have an infinite loop that counts from 0 to 1024 to 0.
You can use itertools for that:
If you want to go to 1024 and back once, you can use:
from itertools import chain
for i in chain(range(0,1024),range(1024,0,-1)):
print(i)
In case you will need this quite often, you can use a function to generate the iterable:
def range_back(start,end):
return chain(range(start,end),range(end,start,-1))
and use it like:
for i in range_back(0,1024):
print(i)
Or if you want to do this an infinite amount of times:
from itertools import chain, cycle
for i in cycle(chain(range(0,1024),range(1024,0,-1))):
print(i)
chain two iterables:
import itertools
for i in itertools.chain(range(1+n), reversed(range(n))):
do_whatever(i)
If you really want infinite loop:
start = 0
end = 1024
i = start
while True:
print(i)
if i==end:
increment = -1
elif i==start:
increment = 1
i += increment
In case you want a solution without using itertools:
while True:
for i in range(1025) + range(1025)[::-1]:
print i
Range is a generator in python 3, so cast it a list before adding or reversing it. Also, use parentheses on print i:
while True:
for i in list(range(1025)) + list(range(1025))[::-1]:
print(i)

How can I limit iterations of a loop?

Say I have a list of items, and I want to iterate over the first few of it:
items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5
Naive implementation
The Python naïf coming from other languages would probably write this perfectly serviceable and performant (if unidiomatic) code:
index = 0
for item in items: # Python's `for` loop is a for-each.
print(item) # or whatever function of that item.
index += 1
if index == limit:
break
More idiomatic implementation
But Python has enumerate, which subsumes about half of that code nicely:
for index, item in enumerate(items):
print(item)
if index == limit: # There's gotta be a better way.
break
So we've about cut the extra code in half. But there's gotta be a better way.
Can we approximate the below pseudocode behavior?
If enumerate took another optional stop argument (for example, it takes a start argument like this: enumerate(items, start=1)) that would, I think, be ideal, but the below doesn't exist (see the documentation on enumerate here):
# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
print(item)
Note that there would be no need to name the index because there is no need to reference it.
Is there an idiomatic way to write the above? How?
A secondary question: why isn't this built into enumerate?
How can I limit iterations of a loop in Python?
for index, item in enumerate(items):
print(item)
if index == limit:
break
Is there a shorter, idiomatic way to write the above? How?
Including the index
zip stops on the shortest iterable of its arguments. (In contrast with the behavior of zip_longest, which uses the longest iterable.)
range can provide a limited iterable that we can pass to zip along with our primary iterable.
So we can pass a range object (with its stop argument) to zip and use it like a limited enumerate.
zip(range(limit), items)
Using Python 3, zip and range return iterables, which pipeline the data instead of materializing the data in lists for intermediate steps.
for index, item in zip(range(limit), items):
print(index, item)
To get the same behavior in Python 2, just substitute xrange for range and itertools.izip for zip.
from itertools import izip
for index, item in izip(xrange(limit), items):
print(item)
If not requiring the index, itertools.islice
You can use itertools.islice:
for item in itertools.islice(items, 0, stop):
print(item)
which doesn't require assigning to the index.
Composing enumerate(islice(items, stop)) to get the index
As Pablo Ruiz Ruiz points out, we can also compose islice with enumerate.
for index, item in enumerate(islice(items, limit)):
print(index, item)
Why isn't this built into enumerate?
Here's enumerate implemented in pure Python (with possible modifications to get the desired behavior in comments):
def enumerate(collection, start=0): # could add stop=None
i = start
it = iter(collection)
while 1: # could modify to `while i != stop:`
yield (i, next(it))
i += 1
The above would be less performant for those using enumerate already, because it would have to check whether it is time to stop every iteration. We can just check and use the old enumerate if don't get a stop argument:
_enumerate = enumerate
def enumerate(collection, start=0, stop=None):
if stop is not None:
return zip(range(start, stop), collection)
return _enumerate(collection, start)
This extra check would have a slight negligible performance impact.
As to why enumerate does not have a stop argument, this was originally proposed (see PEP 279):
This function was originally proposed with optional start
and stop arguments. GvR [Guido van Rossum] pointed out that the function call
enumerate(seqn, 4, 6) had an alternate, plausible interpretation as
a slice that would return the fourth and fifth elements of the
sequence. To avoid the ambiguity, the optional arguments were
dropped even though it meant losing flexibility as a loop counter.
That flexibility was most important for the common case of
counting from one, as in:
for linenum, line in enumerate(source,1): print linenum, line
So apparently start was kept because it was very valuable, and stop was dropped because it had fewer use-cases and contributed to confusion on the usage of the new function.
Avoid slicing with subscript notation
Another answer says:
Why not simply use
for item in items[:limit]: # or limit+1, depends
Here's a few downsides:
It only works for iterables that accept slicing, thus it is more limited.
If they do accept slicing, it usually creates a new data structure in memory, instead of iterating over the reference data structure, thus it wastes memory (All builtin objects make copies when sliced, but, for example, numpy arrays make a view when sliced).
Unsliceable iterables would require the other kind of handling. If you switch to a lazy evaluation model, you'll have to change the code with slicing as well.
You should only use slicing with subscript notation when you understand the limitations and whether it makes a copy or a view.
Conclusion
I would presume that now the Python community knows the usage of enumerate, the confusion costs would be outweighed by the value of the argument.
Until that time, you can use:
for index, element in zip(range(limit), items):
...
or
for index, item in enumerate(islice(items, limit)):
...
or, if you don't need the index at all:
for element in islice(items, 0, limit):
...
And avoid slicing with subscript notation, unless you understand the limitations.
You can use itertools.islice for this. It accepts start, stop and step arguments, if you're passing only one argument then it is considered as stop. And it will work with any iterable.
itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])
Demo:
>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
print item,
...
0 1 2 3 4
Example from docs:
islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G
Why not simply use
for item in items[:limit]: # or limit+1, depends
print(item) # or whatever function of that item.
This will only work for some iterables, but since you specified Lists, it works.
It doesn't work if you use Sets or dicts etc.
Pass islice with the limit inside enumerate
a = [2,3,4,2,1,4]
for a, v in enumerate(islice(a, 3)):
print(a, v)
Output:
0 2
1 3
2 4
Why not loop until the limit or the end of list, whichever occurs earlier, like this:
items = range(10)
limit = 5
for i in range(min(limit, len(items))):
print items[i]
Output:
0
1
2
3
4
short solution
items = range(10)
limit = 5
for i in items[:limit]: print(i)

Python for loop - why does this not infinite loop?

Consider the following snippet of Python code:
x = 14
for k in range(x):
x += 1
At the end of execution, x is equal to 28.
My question: shouldn't this code loop forever? At each iteration, it checks if k is less than x. However, x is incremented within the for loop, so it has a higher value for the next comparison.
range(x) is not a "command". It creates a range object one time, and the loop iterates over that. Changing x does not change all objects that were made using it.
>>> x = 2
>>> k = range(x)
>>> list(k)
[0, 1]
>>> x += 1
>>> list(k)
[0, 1]
no, range(x) will return a list with the items[0,1,2,3,4,5,6,7,8,9,10,11,12,13] these items will be iterated. Each time that the loop body gets evaluated x's value change but this does not affects the list that was already generate.
in other words the collection that you will be iterating will be generated only one time.
It's because python for in loop have different behavior compared to for (in other languages):
range(x) is not executed in every iteration, but at first time, and then for in iterate across its elements.
If you want to change the code to run an infinite loop you can use a while instead (and in that case range(x) is pointless).
The for loop in python is not an iterator, but rather iterates over a sequence / generator i.e. any form of iterable.
Considering this, unless the iterable is infinite, the loop will not be infinite. A sequence cannot be infinite but you can possibly create/utilite an infinite generator. One possibility is to use itertools.count which generates a non-ending sequence of numbers starting from a specified start with a specified interval.
from itertools import count | for(;;) {
for i in count(0): | //An infinite block
#An infinite block | }
Alternatively, you can always utilize while loop to create a classical infinite loop
while True:
#An infinite block

How to go up in a python loop with for

In other languages
for(i=0; i<10; i++){
if(...){
i = 4;
}
}
the loop will go up,
but in python,it doesn't work
for i in range(1, 11):
if ...:
i = 4
So can I go up in a loop with 'for'?
One possibility is that you want to skip items. Everything to do with looping over an index is ugly but here's a way to do that with a while loop.
i = 1
while i < 11:
if predicate(i):
i = 4
i += 1
It's better to loop over items of the list that you want to work with directly and just skip the items that you don't want to deal with.
for item in some_list_of_items:
if not predicate(item):
continue
do_something_with_item(item)
Or use a generator expression to filter the items
for item in (item for item in some_list_of_items if predicate(item)):
do_something_with_item(item)
Python does not permit you to modify your looping variable inline. If you wish to do this you should do the following
i = 0
while i < 10:
if i == 3:
i = 7
print(i)
i += 1
This should have the effect you desire.
You can also do the following:
for i in range(10):
if 2 < i < 7:
continue
print(i)
Both have the same effect.
The problem here is that range(1, 11) returns a list and for...in iterates over the list elements hence changing i to something else doesn't work as expected. Using a while loop should solve your problem.
Mind you that is just a bad idea. Changing iteration variable inside a for loop? In my eyes thats equivalent to a goto statement.
Why don't you just ask what you want to accomplish?
Do you want to filter collection? Use continue statement.
Or do you want to repeat some things more times? Create a repeat loop inside.
Do you want iteration in different order? Well prepare the order beforehand.
The while loop solutions that others posted are correct translations, but that is not a very good idea either.
For this case, you may want to use while loop instead of for loop in Python.
For example:
i = 0
while i < 10:
if ...:
i = 4
Just some food for thought.
The for loop loops over an iterable. Create your own iterable that you can move forward yourself.
iterator = iter(range(11))
for i in iterator:
print 'for i = ', i
try:
print 'next()', iterator.next()
except StopIteration:
continue
>>> foo()
for i = 0
next() 1
for i = 2
next() 3
for i = 4
next() 5
for i = 6
next() 7
for i = 8
next() 9
for i = 10
next()
>>>
xrange() is an iterating version of range()
iterable = xrange(11) would behave as an iterator.
itertools provides nice functions like dropwhile http://docs.python.org/library/itertools.html#itertools.dropwhile
This can proceed your iterator for you.
from itertools import dropwhile
iterator = iter(range(11))
for i in iterator:
if i == 3:
i = dropwhile(lambda x: x<8, iterator).next()
print 'i = ', i
>>> foo()
i = 0
i = 1
i = 2
i = 8
i = 9
i = 10
>>>
dropwhile could be called outside your loop to create the iterator over your iteratator.
Then you can simply call next() on it. Since the for loop and the dropwhile are both calling next() on the same iterator you have some control over it.
You could also implement your own iterator that uses send() to allow you to manipulate the iterator.
http://onlamp.com/pub/a/python/2006/10/26/python-25.html?page=2

Categories