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.

Asked By: apscience

||

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.

Answered By: Taymon
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){
Answered By: Adonais

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).

Answered By: Jeffrey

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.

Answered By: kev

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
''')
Answered By: Karl Knechtel

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
Answered By: rob mayoff

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
Answered By: user2492477

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 ith 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.

Answered By: Chris
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.