98

In C, I would do this:

int i;
for (i = 0;; i++)
  if (thereIsAReasonToBreak(i))
    break;

How can I achieve something similar in Python?

teh_senaus
  • 1,394
  • 16
  • 25
  • 5
    I'm no python expert but `while (true): if reasonneeded(i) break i = i+1` Should work? – hkf Mar 27 '12 at 06:15
  • 4
    possible duplicate of [python unbounded xrange()](http://stackoverflow.com/questions/7186336/python-unbounded-xrange) – wim Mar 27 '12 at 06:20
  • In C this would cause an overflow and not go to "infinity" – Philipp Jul 26 '17 at 14:36
  • 1
    Shouldn't the title of this question be changed to "Looping from 0 to infinity in Python"? You're simply trying to have an "infinite `range`", so you can avoid `while True: i += 1` – SomethingSomething Oct 02 '18 at 13:51

8 Answers8

173

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

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 11
    This also plays nicely with `takewhile` : `for x in takewhile(thereIsAReasonToContinue, count()):` – georg Mar 27 '12 at 07:05
  • For me, `for i in range(sys.maxint)` breaks with a `MemoryError`. You also mentioned `xrange()` which works. – scai Feb 13 '17 at 20:04
  • 4
    @scai, in Python3 `range` replaces Python2's `xrange`. In Python2 `range` creates and returns an actual list of ints. You won't have enough RAM for such a big list – John La Rooy Feb 13 '17 at 20:48
26
def to_infinity():
    index = 0
    while True:
        yield index
        index += 1

for i in to_infinity():
    if i > 10:
        break
ThomDietrich
  • 79
  • 1
  • 7
spicavigo
  • 4,116
  • 22
  • 28
17

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.

wim
  • 338,267
  • 99
  • 616
  • 750
6

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
...
finefoot
  • 9,914
  • 7
  • 59
  • 102
5

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.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
2

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1
def infinity():
    i=0
    while True:
        i+=1
        yield i


for i in infinity():
    if there_is_a_reason_to_break(i):
        break
rekinyz
  • 6,576
  • 2
  • 29
  • 32
  • 1
    Thanks, this was what I was looking for. Cleanest and nicest approach imo. – Marcel Braasch Jan 08 '20 at 15:00
  • Yes, very clean, I hadn't quite grokked the power of `yield` other than for lazy sequence generators, but a friend recently used it to provide a `pushd/popd` function without having to maintain an explicit stack. Very clever, it was. – paxdiablo Apr 15 '20 at 00:57
  • Although I like the approach, infinity is now a range. Zero is not part of infinity if you ask me. Oh, and it is a badly reworded version of [this answer](https://stackoverflow.com/a/9884284/589259). – Maarten Bodewes Aug 04 '21 at 14:11
0
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;
firejox
  • 9
  • 3
  • 1
    There are **7 existing answers** to this question, including a top-voted, accepted answer with over **150 votes**. Why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is _always_ useful on Stack Overflow, but it's _especially_ important here where the question has been resolved to the satisfaction of both the OP and the community. Help future readers out by explaining what your answer does different and under what circumstances it might be preferred. – Jeremy Caney Dec 27 '21 at 00:54