Changing Iteration Variable Inside for Loop in Python

Changing iteration variable inside for loop in Python

Python has a few nice things happening in the background of for loops.
For example:

for i in range(10):

will constantly set i to be the next element in the range 0-10 no matter what.

If you want to do the equivalent in python you would do:

i = 0
while i < 10:
print(i)
if i == 2:
i = 4
else: # these line are
i += 1 # the correct way
i += 1 # note that this is wrong if you want 1,2,4,5,6,7,8,9

If you are trying to convert it to C then you have to remember that the i++ in the for loop will always add to the i.

Python: Is it safe to change iteration variable inside the loop?

Yes. It's totally safe to assign to the loop variable(s) in a for loop.

From The for statement docs:

The for-loop makes assignments to the variables(s) in the target list.
This overwrites all previous assignments to those variables including
those made in the suite of the for-loop:

for i in range(10):
print(i)
i = 5 # this will not affect the for-loop
# because i will be overwritten with the next
# index in the range

This information is also available via help('for')

How to change for-loop iterator variable in the loop in Python?

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.

Changing variable values in for loop?

Actually you don't even need a for loop to showcase this what you call an issue but is actually not.

Take this simpler example:

x = 1
i = x
i += 10
print(x)
print(i)

Ofcourse x is not modified because it holds an immutable value of 1.

Here are immutable types in Python:

  • all primitive types: int, float, bool
  • plus tuple and str

Immutability means no shared reference.
So that when you have:
x = 1
y = 1
doesn't mean that x and y are referring to the same exact value 1 but instead each of the two variables have their "duplicate" instance of the same value. So that changing x doesn't affect y at all.

The same way, when you have:
x = 1
i = x
this is going to create a "brand new value of 1" and assign it to i, so that modifying i doesn't affect variable x at all.

But now, to get the behavior you want you can do something like:

x, y, z = 1, 2, 3
l = [x, y, z]
for i in range(len(l)):
l[i] += 10
x, y, z = l

Or if you really really want to be a little bit quirky, you can do:

x, y, z = 1, 2, 3
for var in locals().keys():
if var in ['x', 'y', 'z']:
locals()[var] += 10

But keep in mind that it is a good design decision from the creators of the language to keep certain types immutable, and so you should work with it instead and completely avoid using locals() as above or something else unnatural.

How to change variables fed into a for loop in list form

"It would seem silly that Python would not have this kind of capability, because if not you wouldn't be able to redefine any variable passed through a for loop if it is part of a list that is being passed through." - That's how most programming languages work. To allow this capability would be bad because it would create something called side-effects, which make code obtuse.

Additionally this is a common programming pitfall because you should keep data out of variable names: see http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html (especially the list of similar questions; even if you aren't dealing with variables names, you are at least trying to deal with the variable namespace). The remedy is to work at "one level higher": a list or set in this case. This is why your original question is not reasonable. (Some versions of python will let you hack the locals() dictionary, but this is unsupported and undocumented behavior and very poor style.)


You can however force python to use side-effects like so:

scores = [99.1, 78.3, etc.]
for i,score in enumerate(scores):
scores[i] = int(score)

the above will round scores down in the scores array. The right way to do this however (unless you are working with hundreds of millions of elements) is to recreate the scores array like so:

scores = [...]
roundedScores = [int(score) for score in scores]

If you have many things you want to do to a score:

scores = [..., ..., ...]

def processScores(scores):
'''Grades on a curve, where top score = 100%'''
theTopScore = max(scores)

def processScore(score, topScore):
return 100-topScore+score

newScores = [processScore(s,theTopScore) for s in scores]
return newScores

sidenote: If you're doing float calculations, you should from __future__ import division or use python3, or cast to float(...) explicitly.


If you really want to modify what is passed in, you can pass in a mutable object. The numbers you are passing in are instances of immutable objects, but if for example you had:

class Score(object):
def __init__(self, points):
self.points = points
def __repr__(self):
return 'Score({})'.format(self.points)

scores = [Score(i) for i in [99.1, 78.3, ...]]
for s in scores:
s.points += 5 # adds 5 points to each score

This would still be a non-functional way to do things, and thus prone to all the issues that side-effects cause.



Related Topics



Leave a reply



Submit