I need to decrement in a python for-loop at a special case (or just don't increment).
In C-like languages, this can be easily accomplished by decrementing the index, or if you have an iterator-like structure you could just "decrement" the iterator. But I have no clue how to achieve this in python.
One solution would be to create a while loop and increment manually, but that would, in my case, bring in lots of extra cases, where just one case is needed when I could decrement.
C Example
for (int i = 0; i < N; ++i) {
if (some_condition) {
i--;
}
}
Python equivalent
for i in range(0, N):
if some_condition:
i -= 1 # need something like this
i = i.prev() # or like this
You can make an iteration loop by yourself. You could easily add a loop index independent from next calls, so that you could even use a skip condition that uses the current index.
skip_iteration = True
it = iter(range(10))
iterating = True
value = next(it)
while iterating:
try:
print(value, end=' ')
if skip_iteration:
# iteration skip code
skip_iteration = False
else:
# normal iteration code
value = next(it)
except StopIteration:
iterating = False
Here only the first iteration is skipped, which outputs :
0 0 1 2 3 4 5 6 7 8 9
This code relies on the fact that the next function raises StopIteration if it reaches the end of the iterator.
If the condition in the if statement is somehow related to the iterator i itself then the loop might not end but if the condition is not depended on i then there shall not be any problem
you can also try skipping that particular iteration by using continue.
Related
Let l be a list. In this python script, is the function len(l) executed every time in the for loop?
for i in range(len(l)):
#do something here
If so, it would be very wasteful when len(l) is large. We should introduce a=len(l) and then use range(a) in the for loop so that len function is only used once.
What about the following case?
for i in range(3+4):
#do something here
Is 3+4 computed every time or just once?
What about the for loop in C such as the following?
for (i = 1; i < 3+4; i++) {
do something here;
}
In Python, the for argument range(len(l)) is evaluated once and produces a range object that acts like a generator when it is requested for each iteration of the loop until it reaches the end of the sequence. Thus len(l) is only evaluated once. Note that depending on what you do in the loop body, a simpler for i in l: might be more appropriate.
In the C expression for (i = 1; i < 3+4; i++), the test clause i < 3+4 is evaluated before each iteration of the loop and since 3+4 is a constant expression, the compiler evaluates it at compile time and just generates code to compare i with 7 before the iteration of the loop.
Note however that if the loop is small, the compiler might expand it into a sequence of operations and remove the tests and increment completely.
In C, I would do this:
int i;
for (i = 0;; i++)
if (thereIsAReasonToBreak(i))
break;
How can I achieve something similar in Python?
Using itertools.count:
import itertools
for i in itertools.count(start=1):
if there_is_a_reason_to_break(i):
break
In Python 2, range() and xrange() were limited to sys.maxsize. In Python 3 range() can go much higher, though not to infinity:
import sys
for i in range(sys.maxsize**10): # you could go even higher if you really want
if there_is_a_reason_to_break(i):
break
So it's probably best to use count().
def to_infinity():
index = 0
while True:
yield index
index += 1
for i in to_infinity():
if i > 10:
break
Simplest and best:
i = 0
while not there_is_reason_to_break(i):
# some code here
i += 1
It may be tempting to choose the closest analogy to the C code possible in Python:
from itertools import count
for i in count():
if thereIsAReasonToBreak(i):
break
But beware, modifying i will not affect the flow of the loop as it would in C. Therefore, using a while loop is actually a more appropriate choice for porting that C code to Python.
Reiterating thg435's comment:
from itertools import takewhile, count
def thereIsAReasonToContinue(i):
return not thereIsAReasonToBreak(i)
for i in takewhile(thereIsAReasonToContinue, count()):
pass # or something else
Or perhaps more concisely:
from itertools import takewhile, count
for i in takewhile(lambda x : not thereIsAReasonToBreak(x), count()):
pass # or something else
takewhile imitates a "well-behaved" C for loop: you have a continuation condition, but you have a generator instead of an arbitrary expression. There are things you can do in a C for loop that are "badly behaved", such as modifying i in the loop body. It's possible to imitate those too using takewhile, if the generator is a closure over some local variable i that you then mess with. In a way, defining that closure makes it especially obvious that you're doing something potentially confusing with your control structure.
If you want to use a for loop, it's possible to combine built-in functions iter (see also this answer) and enumerate for an infinite for loop which has a counter. We're using iter to create an infinite iterator and enumerate provides the counting loop variable. The start value is zero by default, but you can set a different start value with the start argument.
for i, _ in enumerate(iter(bool, True), start=1):
input(i)
Which prints:
1
2
3
4
5
...
If you're doing that in C, then your judgement there is as cloudy as it would be in Python :-)
For a loop that exits on a simple condition check at the start of each iteration, it's more usual (and clearer, in my opinion) to just do that in the looping construct itself. In other words, something like (if you need i after loop end):
int i = 0;
while (! thereIsAReasonToBreak(i)) {
// do something
i++;
}
or (if i can be scoped to just the loop):
for (int i = 0; ! thereIsAReasonToBreak(i); ++i) {
// do something
}
That would translate to the Python equivalent:
i = 0
while not there_is_a_reason_to_break(i):
# do something
i += 1
Only if you need to exit in the middle of the loop somewhere (or if your condition is complex enough that it would render your looping statement far less readable) would you need to worry about breaking.
When your potential exit is a simple one at the start of the loop (as it appears to be here), it's usually better to encode the exit into the loop itself.
def infinity():
i=0
while True:
i+=1
yield i
for i in infinity():
if there_is_a_reason_to_break(i):
break
def natural_numbers():
yield from map(sum, enumerate(iter(int,1)))
for i in natural_numbers():
if there_is_a_reason_to_break(i):
break;
C:
# include <stdio.h>
main()
{
int i;
for (i=0; i<10; i++)
{
if (i>5)
{
i=i-1;
printf("%d",i);
}
}
}
Python:
for i in range(10):
if i>5: i=i-1
print(i)
When we compile C code, it goes into a infinite loop printing 5 forever, whereas in Python it doesn't, why not?
The Python output is:
0 1 2 3 4 5 5 6 7 8
In Python, the loop does not increment i, instead it assigns it values from the iterable object (in this case, list). Therefore, changing i inside the for loop does not "confuse" the loop, since in the next iteration i will simply be assigned the next value.
In the code you provided, when i is 6, it is then decremented in the loop so that it is changed to 5 and then printed. In the next iteration, Python simply sets it to the next value in the list [0,1,2,3,4,5,6,7,8,9], which is 7, and so on. The loop terminates when there are no more values to take.
Of course, the effect you get in the C loop you provided could still be achieved in Python. Since every for loop is a glorified while loop, in the sense that it could be converted like this:
for (init; condition; term) ...
Is equivalent to:
init
while(condition) {
...
term
}
Then your for infinite loop could be written in Python as:
i = 0
while i < 10:
if i > 5:
i -= 1
print i
i += 1
The two constructs are both called for loops but they really aren't the same thing.
Python's version is really a foreach loop. It runs once for each element in a collection.
range(10) produces a list like [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] So the for loop runs once for each member of the collection. It doesn't use the value of i in deciding what the next element is, it always takes the next element in the list.
The c for loop gets translated into the equivalent of
int i = 0
while i < 10:
...
i++;
Which is why you can manipulate the i.
Because your two examples are completely different things.
range(10) in python produces a list of values 0 - 9, then for i in returns each value as i. This is generally referred to as a "for-each" loop. You are operating on a value, not the iterator, when you say i=i-1 in your python example.
http://docs.python.org/tutorial/controlflow.html
The for statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression of numbers (like in Pascal), or giving the user the ability to define both the iteration step and halting condition (as C), Python’s for statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence. For example (no pun intended):
a = ['cat', 'window', 'defenestrate']
for x in a:
print x, len(x)
It is not safe to modify the sequence being iterated over in the loop (this can only happen for mutable sequence types, such as lists). If you need to modify the list you are iterating over (for example, to duplicate selected items) you must iterate over a copy.
Of course the sequence generated by range is not mutable.
I have a python for loop that iterates over a dictionary.
The dictionary is very large. For debugging I want to modify the for loop to run only once. How can I limit the for loop to exit after running once.
for key in dic:
do_some_stuff()
#after the first iteration exit
In java I can modify the for loop in this way:
for (int i = 0; i < n; i++)
doSomeStuff();
will be limited like this:
for (int i = 0; i < n, i < 1; i++)
doSomeStuff();
Just use break:
It terminates the nearest enclosing loop, skipping the optional else
clause if the loop has one.
for key in dic:
do_some_stuff()
break
If you want to run the loop for several times (more than once), you can use a counter:
for i, key in enumerate(dic):
do_some_stuff()
if i > 10:
break
This will run the loop for 11 times before breaking as index (i) begins from 0 and goes till 10 .
Since you are doing debugging, I would like to recommend you the pdb package:
With it, you got much more control over the execution process.
import pdb
pdb.set_trace()
for key in dic:
do_some_stuff()
You can always slice the dictionary iterator using
from itertools import islice
for k in islice(dic, 1):
do_something(k)
Then, to disable it, simply change the 1 to None. Thus you could have a debug flag that decides for you:
please_debug = True
for k in islice(dic, 1 if please_debug else None):
do_something(k)
Suppose I have a for loop:
for i in range(1,10):
if i is 5:
i = 7
I want to change i if it meets certain condition. I tried this but didn't work.
How do I go about it?
For your particular example, this will work:
for i in range(1, 10):
if i in (5, 6):
continue
However, you would probably be better off with a while loop:
i = 1
while i < 10:
if i == 5:
i = 7
# other code
i += 1
A for loop assigns a variable (in this case i) to the next element in the list/iterable at the start of each iteration. This means that no matter what you do inside the loop, i will become the next element. The while loop has no such restriction.
A little more background on why the loop in the question does not work as expected.
A loop
for i in iterable:
# some code with i
is basically a shorthand for
iterator = iter(iterable)
while True:
try:
i = next(iterator)
except StopIteration:
break
# some code with i
So the for loop extracts values from an iterator constructed from the iterable one by one and automatically recognizes when that iterator is exhausted and stops.
As you can see, in each iteration of the while loop i is reassigned, therefore the value of i will be overridden regardless of any other reassignments you issue in the # some code with i part.
For this reason, for loops in Python are not suited for permanent changes to the loop variable and you should resort to a while loop instead, as has already been demonstrated in Volatility's answer.
This concept is not unusual in the C world, but should be avoided if possible.
Nonetheless, this is how I implemented it, in a way that I felt was clear what was happening. Then you can put your logic for skipping forward in the index anywhere inside the loop, and a reader will know to pay attention to the skip variable, whereas embedding an i=7 somewhere deep can easily be missed:
skip = 0
for i in range(1,10):
if skip:
skip -= 1
continue
if i=5:
skip = 2
<other stuff>
Simple idea is that i takes a value after every iteration irregardless of what it is assigned to inside the loop because the loop increments the iterating variable at the end of the iteration and since the value of i is declared inside the loop, it is simply overwritten. You'd probably wanna assign i to another variable and alter it. For e.g,
for i in range(1,10):
if i == 5:
u = 7
and then you can proceed to break the loop using 'break' inside the loop to prevent further iteration since it met the required condition.
Just as timgeb explained, the index you used was assigned a new value at the beginning of the for loop each time, the way that I found to work is to use another index.
For example, this is your original code:
for i in range(1,10):
if i is 5:
i = 7
you can use this one instead:
i = 1
j = i
for i in range(1,10):
i = j
j += 1
if i == 5:
j = 7
also, if you are modifying elements in a list in the for loop, you might also need to update the range to range(len(list)) at the end of each loop if you added or removed elements inside it. The way I do it is like, assigning another index to keep track of it.
list1 = [5,10,15,20,25,30,35,40,45,50]
i = 0
j = i
k = range(len(list1))
for i in k:
i = j
j += 1
if i == 5:
j = 7
if list1[i] == 20:
list1.append(int(100))
# suppose you remove or add some elements in the list at here,
# so now the length of the list has changed
k = range(len(list1))
# we use the range function to update the length of the list again at here
# and save it in the variable k
But well, it would still be more convenient to just use the while loop instead.
Anyway, I hope this helps.