itsme.cvk
itsme.cvk

Reputation: 135

How does 'range()' work internally?

How does the range() differentiate the call being made in this case?

Example:

def ex():
    list = [1,2,3,4]
    for val in range(len(list)):
        print(val)
        break
    for val in range(len(list)):
        print(val)
        break

Output -

0
0

In short, my question is why doesn't the output yield this way?

0
1

During the first call to the range() in the 'first for loop' , the call is 'range(len(list))', and in the first call to the range() in the 'second for loop', the call is 'range(len(list))' which the equivalent to the second call to the range() in the 'first for loop'. How does range() know if the call was from 'second for loop' and not 'first for loop'?

Upvotes: 0

Views: 873

Answers (2)

Rudra
Rudra

Reputation: 1

This is how you can make your own custom range() function:

class Your_self_made_forLoop:
  def __init__(self , start , end):
    self.start = start
    self.end = end
  def __iter__(self):
    return loop(self)
class loop:
  def __init__(self , iterable_obj):
    self.iterable = iterable_obj
  def __iter__(self):
    return self
  def __next__(self):
    if self.iterable.start >= self.iterable.end:
      raise StopIteration
    current = self.iterable.start
    self.iterable.start += 1
    return current

for i in Your_self_made_forLoop(1,6):
  print(i)

Upvotes: 0

chepner
chepner

Reputation: 532268

I'm not sure why you expect range would remember that it had been called previously. The class does not maintain any state about previous calls; it simply does what you ask. Each call to range(x) returns a new range object that provides numbers from 0 to x-1 as you iterate over it. Each call is independent of any previous calls.

To get the behavior you are describing, you need to reuse the same iterator for the range object in each loop.

Python 3.5.1 (default, Apr 18 2016, 11:46:32)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> l = [1,2,3,4]
>>> r = range(len(l))
>>> for val in r:
...   print(val)
...   break
...
0
>>> for val in r:
...   print(val)
...   break
...
0
>>> i = iter(r)
>>> for val in i:
...   print(val)
...   break
...
0
>>> for val in i:
...   print(val)
...   break
...
1

You can image that something like

for x in xs:
    do_something(x)

is short for

i = iter(xs)
while True:
    try:
        x = next(i)
    except StopIteration:
        break
    do_something(x)

iter returns its argument if it is already, in fact, an iterator, so every for loop returns a fresh, start-at-the-beginning iterator when you attempt to iterate over a non-iterator iterable.

Upvotes: 6

Related Questions