5

Why does this not create an infinite loop?

a=5
for i in range(1,a):
  print(i)
  a=a+1

or this

for i in range(1,4):
  print(i)
  i=i-1

or this

for i in range(1,4):
  print(i)
  i=1

Is there any way we can create infinite loops using a for loop? I know there is the while loop for that but I was just curious.

Jongware
  • 22,200
  • 8
  • 54
  • 100
Nischal
  • 53
  • 1
  • 1
  • 7
  • 3
    That's why while loops exists. For loops are iterating through elements of a generator. You can write an infinite generator using the `yield` keyword though. – Gábor Fekete Jan 11 '18 at 11:58

6 Answers6

5

range is a class, and using in like e.g. range(1, a) creates an object of that class. This object is created only once, it is not recreated every iteration of the loop. That's the reason the first example will not result in an infinite loop.

The other two loops are not infinite because, unlike the range object, the loop variable i is recreated (or rather reinitialized) each iteration. The values you assign to i inside the loop will be overwritten as the loop iterates.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
4

Consider a for loop:

for item in iterable:
    print(item)

The idea is that as long as iterable is unchanged, we will loop through each and every item inside iterable once. For example,

for item in [3, 2, 1, 666]:
    print(item)

will output 3 2 1 666. In particular, we find that range(1, 4) is a easy way to represent an iterable [1, 2, 3]. Thus,

for i in range(1, 4):
    print(i)

will output 1 2 3.


Example 1

a=5
for i in range(1,a):
  print(i)
  a=a+1

In this case, range(1,a) is evaluated once, when the loop begins.

Example 2

for i in range(1,4):
  print(i)
  i=i-1

In this case, i is reevaluated every loop, before executing the print and i=i-1 statements within the body of the loop.

Example 3

for i in range(1,4):
  print(i)
  i=1

Just like Example 2, i is reevaluated every loop.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
4

You can't, in this case, update the iterator that your for loop is looping over.


The range in for i in range(a): is actually a function - it takes a value, a, and returns an object that contains the values that it will loop through. Once you've built that object you can change the input variable as much as you'd like, and that object won't change.

Imagine if we made our own similar function called my_range that generates a list (whereas the built in range function generates a range):

def my_range(end):
    my_list = []
    for i in range(end):
        my_list.append(i)
    return my_list

Now if we were to use our new function, like so:

a = 4
for i in my_range(a):
    print(i)
    a += 1

It'd be obvious that we can't update the list object that we're looping over by changing a, because the list that we're looping over has already been made, and isn't being remade on every loop.


Can you make an infinite loop in python? Yes, just add a new entry to the object that you're looping through, e.g.:

my_list = [0]
for i in my_list:
    print(i)
    my_list.append(i+1)

Now we're updating the object that we're looping over.

Ari Cooper-Davis
  • 3,374
  • 3
  • 26
  • 43
2

for loops and the range(..) object

If you write for i in range(..): Python does not translate this into something like for(int i = 0; i < n; i++) (in the C-programming language family).

Furthermore the range object is constructed once, before the for loop. The range(..) object, does not know which variables have been used to construct it. Once constructed, the range is fixed.

It sees range(..) as an iterable object, and each iteration, it takes the next item the iterable yields. So whether you set the variable or not in the for loop, has no effect for the next iteration.

In , range(..) is not a specific object, but a call to construct a list. So if you call range(10) (without the for loop), you get [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].

Why it does not work?

So then why does the examples do not work?

a=5
for i in range(1,a):
  print(i)
  a=a+1

Here we construct range(..) once. After that, the variables based on which it was constructed can change, since the range(..) object does change anymore. Incrementing a thus will not mean the range object will get larger.

for i in range(1,4):
  print(i)
  i=i-1

The for loop each time takes the next item of the iterable. So if we first have collected 1 from the range loop, the next iteration, we collect 2. This is regardless what the value of i is.

for i in range(1,4):
  print(i)
  i=1

For the very same reason: for does not take into account the previous value of i. It only fetches the next item the iterable (here range(..) yields). Since range(..) is fixed, it will simply feed the for loop the next item.

Emulating an infinite loop

So we need to construct an iterable that keeps yielding elements. A way to do this is itertools.count:

from itertools import count

for i in count():
    # ...
    pass

Or in case you are not interested in any value, we can use repeat as well:

from itertools import repeat

for _ in repeat(None):
    # ...
    pass
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
2

range copies the parameters given to it for internal use. So changes to those afterwards have no effect. Same as with the loop variable, which is only created from the internal values every time.

That's different though if you use a mutable object like a list to iterate over:

a = [1,2,3]

for i in a:
    a.append(i)

This loop will indeed run infinitely.

Jeronimo
  • 2,268
  • 2
  • 13
  • 28
  • This will however easily run out of memory. Furthermore some collections (not lists) will raise an exception for good reasons when altering a collection when iterating over it. – Willem Van Onsem Jan 11 '18 at 12:07
  • 1
    Sure. It's not meant for actual "use", just for creating a counter-example which behaves as OP expected. – Jeronimo Jan 11 '18 at 12:08
1

Because a range is either a list (Python2) or a range object both of which are finite. That range is created once before the loop starts. Your loop variable is assigned the next element of the range at the beginning of each iteration, regardless of what you assign it later in the loop body. You need an infinite iterator for an infinite for loop, e.g. itertools.cycle:

from itertools import cycle
for x in cycle(range(5)):
    # endless
user2390182
  • 72,016
  • 6
  • 67
  • 89