user2058002
user2058002

Reputation:

Looping from 1 to infinity in Python

In C, I would do this:

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

How can I achieve something similar in Python?

Upvotes: 108

Views: 187167

Answers (8)

Faraaz Kurawle
Faraaz Kurawle

Reputation: 1148

If you want to persist using for and not while along with facility of changing the value of variable to affect the loop, here's an object oriented solution:

class Count:
    def __init__(self, start):
        self.count = start - 1

    def increment(self):
        self.count += 1

    def set(self, number):
        self.count = number - 1

# Since __next__ method directly increments, to balance that `-1` is used in Count class.

class InfiniteIter:
    def __init__(self, start):
        self.count = Count(start)
    
    def __iter__(self):
        return self

    def __next__(self):
        self.count.increment()
        return self.count
    
for i in InfiniteIter(1):
    print(i.count)

    if i.count == 5:
        i.set(10)

    input() # for testing purpose

Explanation:

  • for loop calls __next__ method after completing each iteration which returns a Count object.

  • Count object has a method set used to set value of the count attribute.

  • Since __next__ method directly increments, to balance that -1 is used in Count class.

  • Count objects count attribute can be used to access value. (Can also be used to set value but due to POINT 3, it has be -1 that number)

Refrence: Python Iterator

Upvotes: -1

firejox
firejox

Reputation: 31

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;

Upvotes: 1

finefoot
finefoot

Reputation: 11234

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

Upvotes: 7

John La Rooy
John La Rooy

Reputation: 304147

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

Upvotes: 183

wim
wim

Reputation: 362647

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.

Upvotes: 18

spicavigo
spicavigo

Reputation: 4224

def to_infinity():
    index = 0
    while True:
        yield index
        index += 1

for i in to_infinity():
    if i > 10:
        break

Upvotes: 28

paxdiablo
paxdiablo

Reputation: 881363

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.

Upvotes: 2

Steve Jessop
Steve Jessop

Reputation: 279245

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.

Upvotes: 5

Related Questions