What is the range() function in Python for?

Asked

Viewed 5,035 times

5

What is the function for range() in Python? What is its real use? It’s just a numerical list?

  • Good to remember that the mathematical notation of the Range is: range([start], stop[, step]).

  • This is not mathematical notation; I believe you meant the signature of the function. And the correct would be range(stop) or range(start, stop[, step]). Right?

  • The notation of the intervals. In fact you’re right...although brackets represent intervals too, the most common in mathematics is to use the line of the real ones.

  • In this case it would be α ∈ [start, stop[ ∀ α = start + k*step, k ∈ N just. In response it seemed that you ended up mixing things up

2 answers

11


The range is an eternal class. It is somewhat similar to a list, but unlike a list itself, the intermediate values are not stored in memory, and are then calculated as it is iterated based on the value of the step (which by default is 1). Thus, a range with an element and a range with a million elements use the same amount of memory, which translates into space savings and better performance.

An example of use:

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

This example shows the numbers from 1 to 9. The first parameter, which is called start is the first number inside the range. The second parameter, called stop is the first number that nay is more in the sequence. The number 10 does not appear in the sequence because it is no longer inside the range, and so that goes from 1 to 9.

It is also possible to specify the value of the step when using a third parameter:

for i in range(1, 10, 2):
    print(i)

This will then show the numbers 1, 3, 5, 7 and 9. That is, skipping 2 in 2.

If only one parameter is specified, this will be the stop and the value 0 shall be assumed for the start. Thus, range(5) corresponds to the numbers from 0 to 4.

The range can also be accessed at arbitrary positions without intermediate values needing to be calculated (for example, range(1, 10)[3]). The resulting value (for positive indices within the range) is easily obtained by means of the formula start + index * step.

6

In Python 3, range, despite being used as a function, including the initial examples of tutorials in Python, is not a function. It is a class:

In [1]: teste = range(10)                                                                                          

In [2]: type(teste)                                                                                                
Out[2]: range

In [3]: type(range)                                                                                                
Out[3]: type

In [4]: isinstance(range, type)                                                                                    
Out[4]: True

range is a class and the objects that are instances of that class, which we can call "ranges" have all the properties of Immutable Sequences in Python.

In Python 2, range was a function, which returned a list (list), python normal:

Python 2.7.15 (default, Oct 15 2018, 15:26:09) 
...
In [1]: teste = range(10)

In [2]: type(teste)
Out[2]: list

In [3]: type(range)
Out[3]: builtin_function_or_method

In [4]: isinstance(range, type)
Out[4]: False

What happens is that at some point it has been found that very rare, if ever, when a numerical sequence is needed you will need to have all the numbers of a sequence at once in memory - as you go through the sequence, you will use one number at a time. So still in Python 2 they created the xrange that it was this specialized class to "look like a function", whose return "behaves like a list", but it is in the verdad a sequence "virtual".

The call a range in Python 3 also does not return an "iterator" in Python is something different - it is an object that provides items in sequence, and which can, for example, be consumed by the command for, but can be run only once. If you try to use the same iterator more than once, in the second use it will be empty.

So the "range" object, as well as the lists, tuples and other sequences is an "iterable": I mean - you can build iterators with it, as many times as you want, and each iterator will generate all the elements independently. The command for creates an iterator from an iterable automatically, but this can also be done explicitly with built-in iter:

In [6]: print([i for i in teste])                                                                                  
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [7]: print([i for i in teste])                                                                                  
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [8]: t2 = iter(teste)                                                                                           

In [9]: print([i for i in t2])                                                                                     
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [10]: print([i for i in t2])                                                                                    
[]

And deep down, despite the class range be defined in C, the Python equivalent of it is very simple. What the call a range returns is an instance of a class equivalent to this one:

class Range:
    def __init__(self, start, stop=None, step=1):
        if stop is None:
             stop = start
             start = 0
        self.start = start
        self.stop = stop
        self.step = step

    def __len__(self):
         return (self.start - self.stop) // self.step

    def __getitem__(self, index):
         if index < 0: 
            index += len(self)
         result = self.start + self.step * index
         if index < 0 or result >= self.stop:
             raise IndexError
         return result

    def __iter__(self):
         i = 0
         while True:
              try:
                   yield self[i]
              except IndexError:
                   break
              i += 1

Ready!

The whole "secret" to calculate a number at a Range position at the time it is requested, without going through the previous ones, is the formula result = self.start + self.step * index.

One interesting thing that is clear in this recreation is the part of the range that is very intuitive to use, but can give a headache when we stop to think: the range Python when called with two parameters uses the first as the beginning and the second as the end. But when called only with a parameter, instead of using this parameter as "start" and leaving the end undefined, it takes the beginning as "0", which makes a lot of sense, and puts the only parameter as the "end".

So it’s possible to do range(10) for numbers from 0 to 10, and range(5, 10), for numbers between 5 and 10.

In this class is not implemented the logic to allow regressive counts with negative indices - but just add different checks on __getitem__ when the self.step for negative - I chose to make the code easier to read. On the other hand as I also do not check the types of the parameters, this class works for floating point numbers as well as for integers:

In [21]: a = Range(0, 2, 0.25)                                                                                     

In [22]: print([i for i in a])                                                                                     
[0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75]

It is worth noting that when implementing the special method __iter__ as a Generator (that is: with a yield), the class becomes "eternal", as I described in the beginning. In each for that an instance of this class Range is used, Python will call __iter__, and have a generator, which will count all available numbers and "expire".

Browser other questions tagged

You are not signed in. Login or sign up in order to post.