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
Related
for i in range (0, 81):
output = send command
while True:
last_byte = last_byte - offset
if last_byte > offset:
output = send command
i+
else:
output = send command
i+
break
I want to increase the iterator every time the send command is executed. Right now it only increases by one when the for loop is executed. Please advise
for i in range(0,10):
print(i)
i +=2
print("increased i", i)
I ran this code and it produced out from 0 to 9. I was expecting it would increase the iterator by 2.
Save a copy of the iterator as a named object. Then you can skip ahead if you want to.
>>> myiter = iter(range(0, 10))
>>> for i in myiter:
print(i)
next(myiter, None)
...
0
2
4
6
8
You can't do this inside a for loop, because every time the loop is restarted it reassigns the variable i regardless of your changes on the variable.
To be able to manipulate your loop counting variable, a good way is to use a while loop and increase the throwaway variable manually.
>>> i = 0
>>> while i < 10 :
... print(i)
... i += 2
... print("increased i", i)
...
0
('increased i', 2)
2
('increased i', 4)
4
...
Additionally, if you want to increase the variable on a period rather than based on some particular condition, you can use a proper slicers to slice the iterable on which you're looping over. For instance, if you have an iterator you can use itertools.islice() if you have a list you can simply use steps while indexing (my_list[start:end:step]).
range() has an optional third parameter to specify the step. Use that to increment the counter by two. For example:
for i in range(0, 10, 2):
print(i)
print("increased i", i)
The reason that you cannot increment i like a normal variable is because when the for-loop starts to execute, a list (or a range object in Python 3+) is created, and i merely represents each value in that object incrementally.
#ilaunchpad Sorry I know it's too late to post this but here is what you were looking for
i=0
for J in range(0,10):
print(i)
i = i + 2
print("increased i", i)
You should not use the same variable throughout in the For statement.
Output
vaibhav#vaibhav-Lenovo-G570:~$ python3 /home/vaibhav/Desktop/Python/pattern_test.py
0
2
4
6
8
10
12
14
16
18
increased i 20
How about this?
for i in range(10):
if i == 3:
i += 1
continue
print(i)
Just adding the continue makes the counter go up - the print result is:
0
1
2
4
5
6
7
8
9
Note that without the continue, the 4 will be printed twice.
I think this answers the question.
I wrote something like this.
while True:
if l < 5:
print "l in while", l
l += 1
else:
break
Now I am having a full control but the backdrop is that I have to check all the conditions.
As there are no answers that allow to use the result of the next iterator, here is my proposed solution, that works with many lists and iterables by using either the enumerate() function or just the iter() function:
x = [1, True, 3, '4', 5.05, 6, 7, 8, 9, 10]
x_enum = enumerate(x)
for idx, elem in x_enum:
if idx == 1: # Example condition to catch the element, where we want to iterate manually
print('$: {}'.format(elem))
idx, elem = next(x_enum)
print('$: {}'.format(elem))
else:
print(elem)
will print:
1
$: True # As you can see, we are in the if block
$: 3 # This is the second print statement, which uses the result of next()
4
5.05
6
7
8
9
10
This is also possible with a simple iterator:
x_iter = iter(x)
for elem in x_iter:
if elem == '4': # Example condition to catch the element, where we want to iterate manually
print('$: {}'.format(elem))
elem = next(x_iter)
print('$: {}'.format(elem))
else:
print(elem)
I want to know if is it possible to change the value of the iterator in its for-loop?
For example I want to write a program to calculate prime factor of a number in the below way :
def primeFactors(number):
for i in range(2,number+1):
if (number%i==0)
print(i,end=',')
number=number/i
i=i-1 #to check that factor again!
My question : Is it possible to change the last two line in a way that when I change i and number in the if block, their value change in the for loop!
Update: Defining the iterator as a global variable, could help me? Why?
Short answer (like Daniel Roseman's): No
Long answer: No, but this does what you want:
def redo_range(start, end):
while start < end:
start += 1
redo = (yield start)
if redo:
start -= 2
redone_5 = False
r = redo_range(2, 10)
for i in r:
print(i)
if i == 5 and not redone_5:
r.send(True)
redone_5 = True
Output:
3
4
5
5
6
7
8
9
10
As you can see, 5 gets repeated. It used a generator function which allows the last value of the index variable to be repeated. There are simpler methods (while loops, list of values to check, etc.) but this one matches you code the closest.
No.
Python's for loop is like other languages' foreach loops. Your i variable is not a counter, it is the value of each element in a list, in this case the list of numbers between 2 and number+1. Even if you changed the value, that would not change what was the next element in that list.
The standard way of dealing with this is to completely exhaust the divisions by i in the body of the for loop itself:
def primeFactors(number):
for i in range(2,number+1):
while number % i == 0:
print(i, end=',')
number /= i
It's slightly more efficient to do the division and remainder in one step:
def primeFactors(number):
for i in range(2, number+1):
while True:
q, r = divmod(number, i)
if r != 0:
break
print(i, end=',')
number = q
The only way to change the next value yielded is to somehow tell the iterable what the next value to yield should be. With a lot of standard iterables, this isn't possible. however, you can do it with a specially coded generator:
def crazy_iter(iterable):
iterable = iter(iterable)
for item in iterable:
sent = yield item
if sent is not None:
yield None # Return value of `iterable.send(...)`
yield sent
num = 10
iterable = crazy_iter(range(2, 11))
for i in iterable:
if not num%i:
print i
num /= i
if i > 2:
iterable.send(i-1)
I would definitely not argue that this is easier to read than the equivalent while loop, but it does demonstrate sending stuff to a generator which may gain your team points at your next local programming trivia night.
It is not possible the way you are doing it. The for loop variable can be changed inside each loop iteration, like this:
for a in range (1, 6):
print a
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
4
4
5
5
6
It does get modified inside each for loop iteration.
The reason for the behavior displayed by Python's for loop is that, at the beginning of each iteration, the for loop variable is assinged the next unused value from the specified iterator. Therefore, whatever changes you make to the for loop variable get effectively destroyed at the beginning of each iteration.
To achieve what I think you may be needing, you should probably use a while loop, providing your own counter variable, your own increment code and any special case modifications for it you may need inside your loop. Example:
a = 1
while a <= 5:
print a
if a == 3:
a = a + 1
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
5
5
6
Yes, we can only if we dont change the reference of the object that we are using. If we can edit the number by accessing the reference of number variable, then what you asked is possible.
A simple example:
a=[1,2,3]
a=a+[4]==>here, a new object is created which plots to different address.
a+=[4]==>here , the same object is getting updated which give us the desired result.
number=10
list1=list(range(2,number+1))
# list1
for i in list1:
print(list1,i)
if (number%i==0):
print(i,end=',')
number=number//i #we can simply replace it with number//=i to edit the number without changing the reference or without creating a new object.
try:
[list1.pop() for i in range(10,0,-1) if(i>number)]
#here pop() method is working on the same object which list created by number refers. so, we can able to change the iterable in the forloop.
except:
continue
i=i-1 #to check that factor again!
I am quite new to Python 2.7 so I had a couple of questions regarding using for loops to while loops.
For example: I am writing this definition
def missingDoor(trapdoor,roomwidth,roomheight,step):
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
for m in hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
for e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
i += 1
return sorted(safezone)
I am trying to turn all the for loops to a while loops, so this is currently what I have written so far. I actually dont know if we say "While e in " works near the middle of the code. But using the while loop rules, will this code do the same as the for loop one?
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
m=0
while m < hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
e=0
while e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
e+=1
i += 1
m+=1
return sorted(safezone)
thanks for any advice or help!
No, your code isn't identical.
While they look similar, for item in list and while item in list will do wildly different things.
for item in list is a syntactic way of saying for every item in the list - do something with is.
while item in list is different - a while loop iterates as long as the condition is true. The condition in this case being item in list. It doesn't update the item each iteration and if you never change what item or list are, it might never terminate. Additionally, if any given item isn't in the list it may terminate prematurely.
If you want to iterate through a list and keep a count, using while is the wrong way to go about it. Use the enumerate() function instead.
enumerate() takes a list, and returns a list of tuples, with each item from the list in order with its index, like so:
for i,m in enumerate(hazardflr):
safetiles.append((m,step))
This small change means you no longer have to track your indices manually.
If you are iterating through every item in a list in Python - use for that's what it is designed to do.
It depends on exactly what givenSteps returns, but in general, no. for x in foo evaluates foo once and then assigns x to be each element of foo in turn. while x in foo: ... x += 1, on the other hand, evaluates foo on every iteration and will end early if foo is not a contiguous sequence. For example, if foo = [0, 1, 2, 5, 6], for will use every value of foo, but while will end after 2, because 3 is not in foo. while will also differ from for if foo contains any non-integral values or values below the starting value.
while aList:
m= hazardflr.pop()
# ...
should be roughly equivelent to your other loop
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.
So I want to do something like this:
for i in range(5):
print(i);
if(condition==true):
i=i-1;
However, for whatever reason, even though I'm decrementing i, the loop doesn't seem to notice. Is there any way to repeat an iteration?
for loops in Python always go forward. If you want to be able to move backwards, you must use a different mechanism, such as while:
i = 0
while i < 5:
print(i)
if condition:
i=i-1
i += 1
Or even better:
i = 0
while i < 5:
print(i)
if condition:
do_something()
# don't increment here, so we stay on the same value for i
else:
# only increment in the case where we're not "moving backwards"
i += 1
Python loop using range are by-design to be different from C/C++/Java for-loops. For every iteration, the i is set the the next value of range(5), no matter what you do to i in between.
You could use a while-loop instead:
i = 0
while i<5:
print i
if condition:
continue
i+=1
But honestly: I'd step back and think again about your original problem. Probably you'll find a better solution as such loops are always error-prone. There's a reason why Python for-loops where designed to be different.
You have a misunderstanding about loops in Python. The for loop doesn't care what you do with i at each iteration, because it is not related to the logic of the loop at all. Modifying i just rebinds a local variable.
You would need to use a while loop to achieve the behaviour you're expecting, where the state of i does affect the control flow of the loop:
import random
i = 0
while i < 5:
print(i)
i += 1
if random.choice([True, False]):
i -= 1
range(5) creates a list with numbers 0 thru 4 in it - [0, 1, 2, 3, 4].
When you run a for loop over it, you are iterating over the list. Doing i-= 1 will only decrement the value of that particular element of the list, and the iteration will continue.
Like the other answers here have suggested, you should use a while loop.
i= 0
while i<5:
# do stuff
if #condition:
i-= 1 # or +
i+= 1
Repeating many other answers, and just for completeness, you will need to use a while loop.
i = 0
while i < 5:
print(i)
if (not condition):
i+=1
If you want to move back an iteration in the loop (instead of repeating an iteration), then use this:
i = 0
while i < 5:
print(i)
if (condition):
i -= 1
else:
i += 1
Essentially, while i < 5 evaluates upon each iteration, and checks if i < 5. Thus by decrementing/not changing i, we get something like this: (values of i)
Not changing: 1->2->3-(condition satisfied)> 3 -> 4 -> 5
Decrementing: 1->2->3-(condition satisfied)>2 -> 3 -> 4 -> 5
The reason why i=i-1 in your for loop doesn't make it repeat the iteration is simple. In the for loop, i is assigned the value of the next item in the for loop. Python could care less about what you do with i, as long as it is able to assign the next item to it. Thus, the for loop for i in <your_iterable>:<do whatever> is closer to this:
_i = 0
_length = len(<your_iterable>)
while _i < _length:
i = _i
_i += 1
<do whatever>
However, in this analogy, you wouldn't be able to access the _ predicated variables (_i,_length). This is how I simplify the logic of the for loop. Note that regardless of what i is assigned to, it will be assigned to _i upon the next iteration, and the loop really doesn't care about what i is.
Utilize a while loop:
i = 0
while i < 5:
print(i)
if condition:
i -= 1
i += 1
As has been mentioned, this is rather unidiomatic Python. Perhaps if you post what you are trying to achieve we can give some better advice.
In Python it's possible to set up a two-way exchange between an iterator (what comes after in in a for..in loop) and its consumer (code inside the loop). To achieve this, you can use send in the consumer code to "inject" a value in a generator. In your case, you can simply send back the current value once the condition is met and wrap the range call in a generator that repeats whatever is sent back to it. Here's some code for you to play, intentionally verbose for clarity:
def repeateble(it):
buf, it = None, iter(it)
while True:
if buf is None:
# the buffer is empty, send them the next elem
val = next(it)
else:
# there's something in the buffer
# let's send that back
val = buf
# send the value and wait what they say
back = yield val
if back:
# they've sent us something!
# give them some dummy value as a result of send()
yield None
# and save what they've sent in a buffer
# for the next iteration
buf = back
else:
# they haven't sent anything
# empty the buffer
buf = None
from random import randint
# create a repeateble generator
rng = repeateble(range(100))
for x in rng:
print(x)
# check "some condition"...
if randint(1, 100) > 80:
print('repeat:')
# send the current value back to the generator
# it will be returned on the next iteration
rng.send(x)
You can use readlines if you're iterating through a file and pull out the previous lines based on a condition.
with open("myfile.txt", "r") as f:
text = f.readlines()
for row in range(0, len(text)):
if re.search("Error", text[row]):
print(text[row-1].strip())