Reputation:
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
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
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
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
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
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
Reputation: 4224
def to_infinity():
index = 0
while True:
yield index
index += 1
for i in to_infinity():
if i > 10:
break
Upvotes: 28
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
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