How do I use a C-style for loop in Python?
Question:
I want to use the traditional C-style for loop in Python. I want to loop through characters of a string, but also know what it is, and be able to jump through characters (e.g. i =
5 somewhere in the code).
for
with range
doesn’t give me the flexibility of an actual for loop.
Answers:
The Python for
loop always has foreach semantics. You can, however, do this:
for i in xrange(10):
print i
This is very much like a C for loop. xrange
(or range
, as it was renamed in Python 3) is a constructor for a Python object that iterates through a range of numbers. See the docs for more information.
for i in range(n):
…is the Python equivalent of the C…
for (i = 0; i < n; i++){
Or well, you can use:
for i in range(a, n, s):
…which is equivalent to…
for (i = a; i < n; i+=s){
You can do the following, given an array a:
for i in range(len(a)):
a[i] = i
That’s the closest Python can get to C-style loops.
You can also give the range
command more arguments; for example,
for i in range(2, len(a), 3)
will start at i = 2, and increment it by 3 as long as the result is less than len(a).
In C
:
for(int i=0; i<9; i+=2)
{
dosomething(i);
}
In python3
:
for i in range(0, 9, 2):
dosomething(i)
You just express the same idea in different languages.
I provide the following entirely facetious solution by way of protest. Note that ‘break’ and ‘continue’ will not work. Also note that the loop body must not be indented.
class For:
def __init__(self, **loop_vars):
self.loop_vars = loop_vars
def __call__(self, arg):
if not hasattr(self, 'condition'):
self.condition = arg
return self
if not hasattr(self, 'update'):
self.update = arg
return self
while eval(self.condition, self.loop_vars, self.loop_vars):
exec arg in self.loop_vars
exec self.update in self.loop_vars
For(i = 1, j = 1)('i * j < 50')('i += 1; j += 1')('''
print i, j
''')
There is no simple, precise equivalent of C’s for
statement in Python. Other answers cover using a Python for
statement with a range, and that is absolutely what you should do when possible.
If you want to be able to modify the loop variable in the loop (and have it affect subsequent iterations), you have to use a while
loop:
i = 0
while i < 7:
if someCondition(i):
i = 5
i += 1
But in that loop, a continue
statement will not have the same effect that a continue
statement would have in a C for
loop. If you want continue
to work the way it does in C, you have to throw in a try
/finally
statement:
i = 0
while i < 7:
try:
if someCondition(i):
i = 5
elif otherCondition(i):
continue
print 'i = %d' % i
finally:
i += 1
As you can see, this is pretty ugly. You should look for a more Pythonic way to write your loop.
UPDATE
This just occurred to me… there is a complicated answer that lets you use a normal Python for
loop like a C-style loop, and allows updating the loop variable, by writing a custom iterator. I wouldn’t recommend this solution for any real programs, but it’s a fun exercise.
Example “C-style” for loop:
for i in forrange(10):
print(i)
if i == 3:
i.update(7)
Output:
0
1
2
3
8
9
The trick is forrange
uses a subclass of int
that adds an update
method. Implementation of forrange
:
class forrange:
def __init__(self, startOrStop, stop=None, step=1):
if step == 0:
raise ValueError('forrange step argument must not be zero')
if not isinstance(startOrStop, int):
raise TypeError('forrange startOrStop argument must be an int')
if stop is not None and not isinstance(stop, int):
raise TypeError('forrange stop argument must be an int')
if stop is None:
self.start = 0
self.stop = startOrStop
self.step = step
else:
self.start = startOrStop
self.stop = stop
self.step = step
def __iter__(self):
return self.foriterator(self.start, self.stop, self.step)
class foriterator:
def __init__(self, start, stop, step):
self.currentValue = None
self.nextValue = start
self.stop = stop
self.step = step
def __iter__(self): return self
def next(self):
if self.step > 0 and self.nextValue >= self.stop:
raise StopIteration
if self.step < 0 and self.nextValue <= self.stop:
raise StopIteration
self.currentValue = forrange.forvalue(self.nextValue, self)
self.nextValue += self.step
return self.currentValue
class forvalue(int):
def __new__(cls, value, iterator):
value = super(forrange.forvalue, cls).__new__(cls, value)
value.iterator = iterator
return value
def update(self, value):
if not isinstance(self, int):
raise TypeError('forvalue.update value must be an int')
if self == self.iterator.currentValue:
self.iterator.nextValue = value + self.iterator.step
For all the ones going "Why?", here’s an example where a C-style loop would be helpful:
remaining_retries = 3
for i in range(num_runs):
success = do_run()
if not success and remaining_retries > 0:
i = i - 1
remaining_retries = remaning_retries - 1
The top answer here is fundamentally incorrect in any true for-looping situation. And the second answer does address the issue, but over complicates things (a C-loop is possible).
Bottom Line, Up Front (BLUF):
## The code:
i = -1
length = 100
while i < length - 1:
i += 1
# ...
## With comments:
# start from -1 to enable continues (adapt as needed)
i = -1
length = 100
while i < length - 1: # the loop
# increment @ 1st line to duplicate C loop and allow normal continues
i += 1
# proceed as if in scope of: for(i=0; i < len(orig_line); i++)
Discussion:
For example, in writing a loop in a process that genuinely deserves a loop (such as across a string or for some algorithm’s internal method), it is often desirable to have a loop and slide structure:
for (int i = 0; i < 100; i++){
char c = my_string[i];
if (c != '(') continue;
while (c != ')') {
// do whatever, collect the content etc.
i++
c = my_string[i];
}
// do something else here
}
HOWEVER, in python:
for i in range(100):
c = my_string[i]
if c != '(':
// do whatever
continue
while c != ')':
// collect the content
i += 1
c = my_string[i]
// do something else here
Generates the i
th value from the range
yield, and therefore does not respect modifications to i
from within the loop!
Opinion:
I can’t think of any other language consistent with this design choice other than the shell languages using the seq
tool. But we are employed to write python
to craft applications, not scripts. That makes this design choice annoying. Bear in mind that languages die due to the accumulation of annoying choices.
It also means that python
doesn’t actually have a for loop; it has iterators and recognizes what we are calling a for loop as a grammatical construct for iterating.
Python is getting fast now as technique and implementation improves. All of us are using it for a variety of problems. Perhaps it is time to give python a real for-loop.
I want to use the traditional C-style for loop in Python. I want to loop through characters of a string, but also know what it is, and be able to jump through characters (e.g. i =
5 somewhere in the code).
for
with range
doesn’t give me the flexibility of an actual for loop.
The Python for
loop always has foreach semantics. You can, however, do this:
for i in xrange(10):
print i
This is very much like a C for loop. xrange
(or range
, as it was renamed in Python 3) is a constructor for a Python object that iterates through a range of numbers. See the docs for more information.
for i in range(n):
…is the Python equivalent of the C…
for (i = 0; i < n; i++){
Or well, you can use:
for i in range(a, n, s):
…which is equivalent to…
for (i = a; i < n; i+=s){
You can do the following, given an array a:
for i in range(len(a)):
a[i] = i
That’s the closest Python can get to C-style loops.
You can also give the range
command more arguments; for example,
for i in range(2, len(a), 3)
will start at i = 2, and increment it by 3 as long as the result is less than len(a).
In C
:
for(int i=0; i<9; i+=2)
{
dosomething(i);
}
In python3
:
for i in range(0, 9, 2):
dosomething(i)
You just express the same idea in different languages.
I provide the following entirely facetious solution by way of protest. Note that ‘break’ and ‘continue’ will not work. Also note that the loop body must not be indented.
class For:
def __init__(self, **loop_vars):
self.loop_vars = loop_vars
def __call__(self, arg):
if not hasattr(self, 'condition'):
self.condition = arg
return self
if not hasattr(self, 'update'):
self.update = arg
return self
while eval(self.condition, self.loop_vars, self.loop_vars):
exec arg in self.loop_vars
exec self.update in self.loop_vars
For(i = 1, j = 1)('i * j < 50')('i += 1; j += 1')('''
print i, j
''')
There is no simple, precise equivalent of C’s for
statement in Python. Other answers cover using a Python for
statement with a range, and that is absolutely what you should do when possible.
If you want to be able to modify the loop variable in the loop (and have it affect subsequent iterations), you have to use a while
loop:
i = 0
while i < 7:
if someCondition(i):
i = 5
i += 1
But in that loop, a continue
statement will not have the same effect that a continue
statement would have in a C for
loop. If you want continue
to work the way it does in C, you have to throw in a try
/finally
statement:
i = 0
while i < 7:
try:
if someCondition(i):
i = 5
elif otherCondition(i):
continue
print 'i = %d' % i
finally:
i += 1
As you can see, this is pretty ugly. You should look for a more Pythonic way to write your loop.
UPDATE
This just occurred to me… there is a complicated answer that lets you use a normal Python for
loop like a C-style loop, and allows updating the loop variable, by writing a custom iterator. I wouldn’t recommend this solution for any real programs, but it’s a fun exercise.
Example “C-style” for loop:
for i in forrange(10):
print(i)
if i == 3:
i.update(7)
Output:
0
1
2
3
8
9
The trick is forrange
uses a subclass of int
that adds an update
method. Implementation of forrange
:
class forrange:
def __init__(self, startOrStop, stop=None, step=1):
if step == 0:
raise ValueError('forrange step argument must not be zero')
if not isinstance(startOrStop, int):
raise TypeError('forrange startOrStop argument must be an int')
if stop is not None and not isinstance(stop, int):
raise TypeError('forrange stop argument must be an int')
if stop is None:
self.start = 0
self.stop = startOrStop
self.step = step
else:
self.start = startOrStop
self.stop = stop
self.step = step
def __iter__(self):
return self.foriterator(self.start, self.stop, self.step)
class foriterator:
def __init__(self, start, stop, step):
self.currentValue = None
self.nextValue = start
self.stop = stop
self.step = step
def __iter__(self): return self
def next(self):
if self.step > 0 and self.nextValue >= self.stop:
raise StopIteration
if self.step < 0 and self.nextValue <= self.stop:
raise StopIteration
self.currentValue = forrange.forvalue(self.nextValue, self)
self.nextValue += self.step
return self.currentValue
class forvalue(int):
def __new__(cls, value, iterator):
value = super(forrange.forvalue, cls).__new__(cls, value)
value.iterator = iterator
return value
def update(self, value):
if not isinstance(self, int):
raise TypeError('forvalue.update value must be an int')
if self == self.iterator.currentValue:
self.iterator.nextValue = value + self.iterator.step
For all the ones going "Why?", here’s an example where a C-style loop would be helpful:
remaining_retries = 3
for i in range(num_runs):
success = do_run()
if not success and remaining_retries > 0:
i = i - 1
remaining_retries = remaning_retries - 1
The top answer here is fundamentally incorrect in any true for-looping situation. And the second answer does address the issue, but over complicates things (a C-loop is possible).
Bottom Line, Up Front (BLUF):
## The code:
i = -1
length = 100
while i < length - 1:
i += 1
# ...
## With comments:
# start from -1 to enable continues (adapt as needed)
i = -1
length = 100
while i < length - 1: # the loop
# increment @ 1st line to duplicate C loop and allow normal continues
i += 1
# proceed as if in scope of: for(i=0; i < len(orig_line); i++)
Discussion:
For example, in writing a loop in a process that genuinely deserves a loop (such as across a string or for some algorithm’s internal method), it is often desirable to have a loop and slide structure:
for (int i = 0; i < 100; i++){
char c = my_string[i];
if (c != '(') continue;
while (c != ')') {
// do whatever, collect the content etc.
i++
c = my_string[i];
}
// do something else here
}
HOWEVER, in python:
for i in range(100):
c = my_string[i]
if c != '(':
// do whatever
continue
while c != ')':
// collect the content
i += 1
c = my_string[i]
// do something else here
Generates the i
th value from the range
yield, and therefore does not respect modifications to i
from within the loop!
Opinion:
I can’t think of any other language consistent with this design choice other than the shell languages using the seq
tool. But we are employed to write python
to craft applications, not scripts. That makes this design choice annoying. Bear in mind that languages die due to the accumulation of annoying choices.
It also means that python
doesn’t actually have a for loop; it has iterators and recognizes what we are calling a for loop as a grammatical construct for iterating.
Python is getting fast now as technique and implementation improves. All of us are using it for a variety of problems. Perhaps it is time to give python a real for-loop.