**This tutorial explains what is Python Range function and how to use it in your programs. Also learn the differences between range() and xrange():**

A range is a close interval between two points. We use ranges everywhere i.e. from the **1st** to **31st**, from **August** to **December,** or from **10** to **15**. Ranges help us to enclose a group of numbers, letters, etc which we can use later for different needs.

In Python, there is an inbuilt function called **range()** that returns an object that produces a sequence of numbers(integers) that will be later on used in our program.

**=> Check ALL Python Tutorials Here**

What You Will Learn:

## The Python range() Function

The **range()** function returns a generator object that can produce a sequence of integers.

In this section, we will discuss the Python **range()** function and its syntax**. **Before we delve into the section, it is important to note that Python **2.x** has 2 types of range functions i.e. the **xrange()** and the **range(). **Both of them are called and used in the same way but with different output.

The **range()** was dropped and **xrange()** was re-implemented in **Python 3.x** and named **range()**. We will get into **xrange()** later on and for now we will focus on **range()**.

### The Python range() Syntax

As mentioned before, a **range** is a sequence of integers between 2 endpoints.

**To get the syntax of range, we can look at its docstring from the terminal with the below command:**

>>> range.__doc__ 'range(stop) -> range object\nrange(start, stop[, step]) -> range object\n\nReturn an object that produces a sequence of integers from start (inclusive)\nto stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\nstart defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\nThese are exactly the valid indexes for a list of 4 elements.\nWhen step is given, it specifies the increment (or decrement).'

**Notice the first line**

range(stop) -> range object\nrange(start, stop[, step]) -> range

### Different Ways to Construct Range

The above syntax shows that the **range() **function can take up to 3 parameters.

** This provides Python range() syntax with about 3 different ways of implementation as shown below.**

**NB**: We should note the following default values for the different parameters.

- start defaults to 0
- step defaults to 1
- stop is required.

#### #1) range(stop)

As seen above, the **range** function takes a stop parameter(exclusive) which is an integer that indicates where the range will end. Therefore if you use range(7), it will display all the integers from 0 to 6.

In a nutshell, whenever the **range()** is given a single argument, that argument represents the stop parameter, and the start and step parameters adopt their default values.

**Example 1:** Print a range of integers from 0 to 6.

>>> list(range(7)) [0, 1, 2, 3, 4, 5, 6]

#### #2) range(start, stop)

Here, the **range()** function is called with two parameters (start and stop). These parameters can be any integer where the start is greater than stop (start > stop). The first parameter (start) is the starting point of the range and the other parameter(stop) is the exclusive end of the range.

**NB**: The stop parameter is **exclusive**. **For example,** range(5,10) will result in a sequence from 5 to 9, excluding 10.

**Example 2:** Find the range between two numbers, where start=5 and stop=10

>>> list(range(5,10)) [5, 6, 7, 8, 9]

#### #3) range(start, stop, step)

Here, when the **range() **receives 3 arguments, the arguments represent the start, stop and step parameters from left to right.

When the sequence of numbers is created, the first number will be the start argument, and the last number of the sequence will be a number before the stop argument, represented as a stop – 1.

The step argument indicates how many “steps” will separate each number in the sequence. It could be incremental or decremental steps.

We should recall that by default, the step parameter defaults to 1. So, if by any chance we want it to be a 1, then we can decide to provide it explicitly or omit it.

**NB: **The step argument can’t be 0 or a** floating-point number.**

Consider the example below where start=5, stop=15, and step=3

**Example 3**: Find a range of sequence from 5 to 14, having an increment of 3

>>> list(range(5,15,3)) [5, 8, 11, 14]

** Using Negative steps with range()**

The step parameter of the **range()** function can be a negative integer that is range(30, 5, -5). As seen in the below figure, when using a **negative step**, the start parameter has to be higher than the stop parameter. If not, the resulting sequence will be empty.

The counter will count from the start while using the step to jump over to the next value.

**Example 4**: Let’s see how a negative step works when the start is greater or smaller than the stop.

>>> list(range(30,5,-5)) # start > stop [30, 25, 20, 15, 10] >>> list(range(5,30,-5)) # start < stop []

### How to Use Python range()

The range has its place in Python and it is often used in many programs. In this section, we shall exploit some of the ways in which it can be used.

### Using Python range() in Loops

The for loop is one of the most common areas where **range()** is used. A for loop statement is the one that iterates through a collection of items. To learn more about Python loops and the for loop, read through the tutorial **Loops in Python**.

**Example 5**: Using a **for loop** and r**ange()**, print a sequence of numbers from 0 to 9.

def rangeOfn(n): for i in range(n): print(i) if __name__ == '__main__': n = 10 rangeOfn(n)

**Output**

**Example 5** given above uses the **range(stop)** syntax. This returns a generator object which is fed into the for loop, which iterates through the object, extracting the items and printing them.

**Example 6**: Using a **for loop** and r**ange()**, print a sequence of numbers from 5 to 9.

This example uses the **range(start, stop)** syntax, where the start will define where the loop will begin(Inclusive) and the stop where the loop will end(stop-1)

def rangeFromStartToStop(start, stop): for i in range(start, stop): print(i) if __name__ == '__main__': start = 5 # define our start value stop = 10 # define our stop value rangeFromStartToStop(start, stop)

**Output**

**Example 7**: Using a **for loop** and r**ange()**, print a sequence of numbers from 5 to 9 and an increment of 2.

This example uses the** range(start, stop, step)** syntax in the for statement. The for statement will begin the count at the start parameter and will jump to the next value according to the step integer and will end at stop-1.

def rangeFromStartToStopWithStep(start, stop, step): for i in range(start, stop, step): print(i) if __name__ == '__main__': start = 5 # define our start value stop = 10 # define our stop value step = 2 # define our increment rangeFromStartToStopWithStep(start, stop, step)

**Output**

For our last example in this section, we shall look at how iterables are commonly iterated. Consider the example below.

**Example 8**: Iterate through the list [3,2,4,5,7,8] and print all its items.

def listItems(myList): # use len() to get the length of the list # the length of the list represents the 'stop' argument for i in range(len(myList)): print(myList[i]) if __name__ == '__main__': myList = [3,2,4,5,7,8] # define our list listItems(myList)

**Output**

### Using range() with Data Structures

As we mentioned earlier in this tutorial, the **range()** function returns an object (of type **range**) that produces a sequence of integers from start (inclusive) to stop (exclusive) by step.

Hence, running the **range()** function on its own will return a range object which is iterable. This object can easily be converted into various data structures like List, Tuple, and Set as shown below.

**Example 9**: Construct a **list** with a sequence of integers from 4 to 60 (**inclusive**), and an increment of 4.

>>> list(range(4, 61, 4)) # our 'stop' argument is 61 because 60 is inclusive. [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60]

From **example 9** above, all we had to do is call our range function in the **list()** constructor.

**Example 10**: Construct a **tuple** with a sequence of integers from 4 to 60 (**inclusive**), and an increment of 4.

>>> tuple(range(4, 61, 4)) # enclose in the tuple() constructor (4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60)

**Example 11**: Construct a **set** with a sequence of integers from 4 to 60 (**inclusive**) and an increment of 4.

>>> set(range(4, 61, 4)) # enclose in the set() constructor {32, 4, 36, 8, 40, 12, 44, 60, 16, 48, 20, 52, 24, 56, 28}

**NB**: Notice how the resulting sequence of integers is unordered. This is because a set is an unordered collection.

This **example 11** may seem useless at first since the range object will always return a sequence of unique integers. So, we may ask ourselves, why enclosing in a** set()** constructor. Well, imagine that you need to have a default set containing a sequence of integers in which you will later add some items.

## Python xrange()

As mentioned before **xrange()** is a Python **2.x** function which acts as the **range()** function in the **3.x** Python version. The only similarity between these two functions is that they produce a sequence of numbers and can use the start, stop, and step parameters.

It is important to know that, in Python **2.x**, both **range()** and** xrange() **are defined, where **range()** returns a list object while **xrange()** returns a range object. However, migrating to Python **3.x**, the range was dissolved and xrange was re-implemented and named range.

**Example 12**: Return value of **range** and **xrange** in Python **2.x**

>>> xr = xrange(1,4) >>> xr # output the object created xrange(1, 4) >>> type(xr) # get type of object <type 'xrange'> >>> r = range(1,4) >>> r # output the object created [1, 2, 3] >>> type(r) # get type of object <type 'list'>

## Difference Between range() And xrange()

In this section, we shall not look much at the difference between **xrange()** and **range()** in Python **2.x**. However, we shall look at the difference between **xrange()** of Python **2.x** and **range()** of Python** 3.x**.

Though **xrange()** was re-implemented in Python **3.x** as** range()**, it added some features to it and that made it different from its predecessor.

The differences between **range()** and **xrange()** can be related to operational differences, memory consumption, returned type, and performance. But in this section, we shall look at the operational differences and memory consumption.

**NB**:

- Code in this section will be run on the Python shell terminal. Given that we have both Python
**2**and**3**installed, we can access Python**2**shell with the command.

python2

Python **3** shell terminal with the command.

python3

- All code related to
**xrange**should be run on the Python**2**shell while all code related to the**range**should be run on the Python**3**shell.

### #1) Operational Differences

**xrange** and **range** operate the same way. They both have the same syntax and return objects that can produce sequences of integers.

**Example 13**: Operational difference between **xrange** and **range**

**Solution 13.1**: Python 3.x

>>> r = range(3,8,2) # create range >>> r range(3, 8, 2) >>> type(r) # get type <class 'range'> >>> list(r) # convert to list [3, 5, 7] >>> it = iter(r) # get iterator >>> next(it) # get next 3 >>> next(it) # get next 5

**Solution 13.2**: Python 2.x

>>> xr = xrange(3,8,2) # create xrange >>> xr # notice how it is represented below with 9 instead of 8. xrange(3, 9, 2) >>> type(xr) # get type. Here it is of type 'xrange' <type 'xrange'> >>> list(xr) # get list [3, 5, 7] >>> it = iter(xr) # get iterator >>> it.next() # get next 3 >>> next(it) # get next 5

From the solutions above, we see that the types are named differently. Also, the stop argument is incremented for **xrange**. Both can return an iterator from iter() but the iter built-in next() method works only for **xrange** while both support the built-in **next()** function.

In this scenario, both operate precisely in the same way. However, we have some list operations that can apply to the **range** but not on **xrange**. Recall that Python **2.x** had both **xrange** and **range** but the **range** here was of the type **list**.

So, while migrating to Python **3.x**, xrange was re-implemented and some of the range properties were added to it.

**Example 14**: Check if **xrange** and **range** support indexing and slicing.

**Solution 14.1**: Python 3.x

>>> r = range(3,8,2) # create range >>> r # print object range(3, 8, 2) >>> list(r) # return list of object [3, 5, 7] >>> r[0] # indexing, returns an integer 3 >>> r[1:] # slicing, returns a range object range(5, 9, 2) >>> list(r[1:]) # get list of the sliced object [5, 7]

**Solution 14.2: **Python 2.x

>>> xr = xrange(3,8,2) # create xrange >>> xr # print object xrange(3, 9, 2) >>> list(xr) # get list of object [3, 5, 7] >>> xr[0] # indexing, return integer 3 >>> xr[1:] # slicing, doesn't work Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sequence index must be integer, not 'slice'

We can conclude that **xrange** doesn’t support slicing.

### #2) Memory Consumption

Both xrange and range have static memory storage for their objects. However, **xrange** consumes less memory than **range**.

**Example 15**: Check the memory consumed by both xrange as well as range.

**Solution 15.1**: Python 3.x

>>> import sys # import sys module >>> r = range(3,8,2) # create our range >>> sys.getsizeof(r) # get memory occupied by object 48 >>> r2 = range(1,3000000) # create a wider range >>> sys.getsizeof(r2) # get memory, still the same 48

**Solution 15.2**: Python 2.x

>>> import sys >>> xr = xrange(3,8,2) >>> sys.getsizeof(xr) # get memory size 40 >>> xr2 = xrange(1, 3000000) # create wider range >>> sys.getsizeof(xr2) # get memory 40

We see that **xrange** objects occupy a memory size of 40, unlike a range that occupies **48**.

## range() in Numpy

Numpy is a Python library for numerical computation. Numpy provides a variety of methods to create arrays in which the arange() function is a part.

### Installation

We can first check if Numpy is already installed in our system by running the below command.

>>> Import numpy

If we get the ModuleNotFoundError exception, then we have to get it installed. One way is to use pip as shown below;

>>> pip install numpy

### Syntax

numpy.arange([start, ]stop, [step, ]dtype=None) -> numpy.ndarray

From the syntax above, we see the similarity with the Python** range()**. But in addition to this parameter, the Python** arange()** also gets the dtype which defines the type of the return array.

Also, it returns a numpy.ndarray rather than a decorator object like Python **range()**.

**Example 16**: Check returned type of **numpy.arange()**

>>> import numpy as np # import numpy >>> nr = np.arange(3) # create numpy range >>> nr # display output, looks like an array array([0, 1, 2]) >>> type(nr) # check type <class 'numpy.ndarray'>

The four parameters in** arange()** are the data type (**dtype) **which define the numeric built-in value in the return array. The **dtypes** offered by numpy differs in memory used and have limits as seen in the table below.

**Table on numpy data types (dtype)**

Date Type (dtype) | Description |
---|---|

np.int8 | 8-bit integer Range from -128 to 127 |

np.unit8 | 8-bit unsigned integer Range from 0 to 255 |

np.int16 | 16-bit integer Range from 32768 to 32767 |

np.unit16 | 16-bit unsigned integer Range from 0 to 65535 |

np.int32 | 32-bit integer Range from -2**31 to 2**31-1 |

np.unit32 | 32-bit unsigned integer Range from 0 to 2**32-1 |

np.int64 | 64-bit integer Range from -2**63 to 2**63-1 |

np.unit64 | 64-bit unsigned integer Range from 0 to 2**64-1 |

**Example 17**: Using dtype of 8bits integer

>>> import numpy as np >>> x = np.arange(2.0, 16, 4, dtype=np.int8) # start is float >>> x # but output is int8 stated by dtype array([ 2, 6, 10, 14], dtype=int8) >>> x.dtype # check dtype dtype('int8')

If **dtype** is not assigned, then the **dtype** of the resulting array will be determined based on the step, stop and step arguments.

If all the arguments are integers, then the **dtype** will be **int64. **However, if the data type changes to the floating-point in any of the arguments, then the **dtype** will be **float64**.

### Difference Between numpy.arange() And range()

**range()**is a built-in Python class while**numpy.arange()**is a function that belongs to the**Numpy**library.- Both collect the start, stop and step parameters. The only difference comes in when the dtype is defined in the
**numpy.arange()**thereby making it able to use 4 parameters while**range()**uses only 3. - The return types are different:
**range()**returns a Python class range while**numpy.arange()**returns an instance of**Numpy**ndarray. These return types are better than each other depending on the situations they are required in. **numpy.arange()**supports floating-point numbers for all its parameters while range supports only integers.

Before we round up this section, it is important to know that as numpy.arange doesn’t return a decorator object like **range()**, it has a limitation in the range of sequence it can generate.

**Example 18**: Show numpy.arange limitation

**NB**: Please don’t try this, or it may take forever to run or just crash your system.

>>> np.arange(1, 90000000000)

## Frequently Asked Questions

**Q #1) How to turn a range() to a list in Python3**

**Answer:** To change a range to a list in Python **3.x** you will just need to call a list encapsulating the range function as below.

>>> list(range(4,16,2)) [4, 6, 8, 10, 12, 14]

**Q #2) How does the Python range work?**

**Answer:** Basically, Python range takes in three parameters namely i.e. start, stop and step and creates a sequence of integers starting from the start, ending at stop-1 and incremented or decremented by step.

Python **range()** works differently based on the Python version. In Python **2.x**, **range()** returns a **list** while in Python **3.x**, a **range** object is returned.

**Q #3) Explain the Error “xrange not defined” while running in python3.**

**Answer: **This error occurs because **xrange()** is not a built-in function in Python **3.x**. The **xrange()** function is instead built-in in Python **2.x **but was re-implemented in Python **3.x** and named **range**.

## Conclusion

In this tutorial, we looked at Python **range()** and its syntax. We examined the different ways in which we can construct a range based on the number of parameters provided. We also looked at how Python **range()** is used in a loop like f**or loop** and data structures like **list**, **tuple,** and **set**.

Down the line, we looked at the differences between **xrange** in Python** 2.x **and range in Python **3.x**. Finally, we had a glance at how** the range **is implemented in **Numpy**.

**=> Visit Here To Learn Python From Scratch**