The range objects are special:

Python will compare range objects as Sequences. What that essentially means is that the comparison doesn't evaluate how they represent a given sequence but rather what they represent.

The fact that the start, stop and step parameters are completely different plays no difference here because they all represent an empty list when expanded:

For example, the first range object:

list(range(0))  # []

and the second range object:

list(range(2, 2, 2)) # []

Both represent an empty list and since two empty lists compare equal (True) so will the range objects that represent them.

As a result, you can have completely different looking range objects; if they represent the same sequence they will compare equal:

range(1, 5, 100) == range(1, 30, 100) 

Both represent a list with a single element [1] so these two will also compare equal.


No, range objects are really special:

Do note, though, that even though the comparison doesn't evaluate how they represent a sequence the result of comparing can be achieved using solely the values of start, step along with the len of the range objects; this has very interesting implications with the speed of comparisons:

r0 = range(1, 1000000)    
r1 = range(1, 1000000)

l0 = list(r0)    
l1 = list(r1)

Ranges compares super fast:

%timeit r0 == r1
The slowest run took 28.82 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 160 ns per loop

on the other hand, the lists..

%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop

Yeah..


As @SuperBiasedMan noted, this only applies to the range objects in Python 3. Python 2 range() is a plain ol' function that returns a list while the 2.x xrange object doesn't have the comparing capabilies (and not only these..) that range objects have in Python 3.

Look at @ajcr's answer for quotes directly from the source code on Python 3 range objects. It's documented in there what the comparison between two different ranges actually entails: Simple quick operations. The range_equals function is utilized in the range_richcompare function for EQ and NE cases and assigned to the tp_richcompare slot for PyRange_Type types.

I believe the implementation of range_equals is pretty readable (because it is nice as simple) to add here:

/* r0 and r1 are pointers to rangeobjects */

/* Check if pointers point to same object, example:    
       >>> r1 = r2 = range(0, 10)
       >>> r1 == r2
   obviously returns True. */
if (r0 == r1)
    return 1;

/* Compare the length of the ranges, if they are equal 
   the checks continue. If they are not, False is returned. */
cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ);
/* Return False or error to the caller
       >>> range(0, 10) == range(0, 10, 2)  
   fails here */
if (cmp_result != 1)
    return cmp_result;

/* See if the range has a lenght (non-empty). If the length is 0
   then due to to previous check, the length of the other range is 
   equal to 0. They are equal. */
cmp_result = PyObject_Not(r0->length);
/* Return True or error to the caller. 
       >>> range(0) == range(2, 2, 2)  # True
   (True) gets caught here. Lengths are both zero. */
if (cmp_result != 0)
    return cmp_result;

/* Compare the start values for the ranges, if they don't match
   then we're not dealing with equal ranges. */
cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ);
/* Return False or error to the caller. 
   lens are equal, this checks their starting values
       >>> range(0, 10) == range(10, 20)  # False
   Lengths are equal and non-zero, steps don't match.*/
if (cmp_result != 1)
    return cmp_result;

/* Check if the length is equal to 1. 
   If start is the same and length is 1, they represent the same sequence:
       >>> range(0, 10, 10) == range(0, 20, 20)  # True */
one = PyLong_FromLong(1);
if (!one)
    return -1;
cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ);
Py_DECREF(one);
/* Return True or error to the caller. */
if (cmp_result != 0)
    return cmp_result;

/* Finally, just compare their steps */
return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ);

I've also scattered some of my own comments here; look at @ajcr's answer for the Python equivalent.

Answer from Dimitris Fasarakis Hilliard on Stack Overflow
🌐
W3Schools
w3schools.com › python › ref_func_range.asp
Python range() Function
Python Examples Python Compiler ... Q&A Python Bootcamp Python Training ... The range() function returns a sequence of numbers, starting from 0 by default, and ......
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-range-function
Python range() function - GeeksforGeeks
The range() function in Python is used to generate a sequence of integers within a specified range. It is most commonly used in loops to control how many times a block of code runs.
Published   March 10, 2026
Discussions

Understanding Range() function in python. [Level: Absolute Beginner]
You can think of range as returning a sequence of numbers. Since we use the syntax for in : to loop or iterate over a sequence, naturally we can put a range as the sequence. Does that help? More on reddit.com
🌐 r/learnpython
23
4
August 6, 2024
Why is range(0) == range(2, 2, 2) True in Python 3? - Stack Overflow
Why do ranges which are initialized with different values compare equal to one another in Python 3? When I execute the following commands in my interpreter: >>> r1 = range(0) >>&... More on stackoverflow.com
🌐 stackoverflow.com
python - Looping over range(0) - Stack Overflow
Here is the function: def is_sorted(L): """ (str) -> Bool Return True iff the L is sorted in nondecreasing order. Otherwise, return False. >>> is_sorted([1, 2, 3, 3]... More on stackoverflow.com
🌐 stackoverflow.com
Why is range(5) giving me range(0,5) instead of 0 1 2 3 4

Because that's how it's represented. It doesn't actually store all those values, only start, stop, and step.

More on reddit.com
🌐 r/learnpython
28
0
October 25, 2022
🌐
Stanford CS
cs.stanford.edu › people › nick › py › python-range.html
Python range() Function
print(i, s[i]) ... 5 n 4 o 3 h 2 t 1 y 0 P · Range with 2 parameters specifies a start number other than 0, but is otherwise like the 1 parameter form above, going up to but not including the stop number.
Top answer
1 of 5
88

The range objects are special:

Python will compare range objects as Sequences. What that essentially means is that the comparison doesn't evaluate how they represent a given sequence but rather what they represent.

The fact that the start, stop and step parameters are completely different plays no difference here because they all represent an empty list when expanded:

For example, the first range object:

list(range(0))  # []

and the second range object:

list(range(2, 2, 2)) # []

Both represent an empty list and since two empty lists compare equal (True) so will the range objects that represent them.

As a result, you can have completely different looking range objects; if they represent the same sequence they will compare equal:

range(1, 5, 100) == range(1, 30, 100) 

Both represent a list with a single element [1] so these two will also compare equal.


No, range objects are really special:

Do note, though, that even though the comparison doesn't evaluate how they represent a sequence the result of comparing can be achieved using solely the values of start, step along with the len of the range objects; this has very interesting implications with the speed of comparisons:

r0 = range(1, 1000000)    
r1 = range(1, 1000000)

l0 = list(r0)    
l1 = list(r1)

Ranges compares super fast:

%timeit r0 == r1
The slowest run took 28.82 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 160 ns per loop

on the other hand, the lists..

%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop

Yeah..


As @SuperBiasedMan noted, this only applies to the range objects in Python 3. Python 2 range() is a plain ol' function that returns a list while the 2.x xrange object doesn't have the comparing capabilies (and not only these..) that range objects have in Python 3.

Look at @ajcr's answer for quotes directly from the source code on Python 3 range objects. It's documented in there what the comparison between two different ranges actually entails: Simple quick operations. The range_equals function is utilized in the range_richcompare function for EQ and NE cases and assigned to the tp_richcompare slot for PyRange_Type types.

I believe the implementation of range_equals is pretty readable (because it is nice as simple) to add here:

/* r0 and r1 are pointers to rangeobjects */

/* Check if pointers point to same object, example:    
       >>> r1 = r2 = range(0, 10)
       >>> r1 == r2
   obviously returns True. */
if (r0 == r1)
    return 1;

/* Compare the length of the ranges, if they are equal 
   the checks continue. If they are not, False is returned. */
cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ);
/* Return False or error to the caller
       >>> range(0, 10) == range(0, 10, 2)  
   fails here */
if (cmp_result != 1)
    return cmp_result;

/* See if the range has a lenght (non-empty). If the length is 0
   then due to to previous check, the length of the other range is 
   equal to 0. They are equal. */
cmp_result = PyObject_Not(r0->length);
/* Return True or error to the caller. 
       >>> range(0) == range(2, 2, 2)  # True
   (True) gets caught here. Lengths are both zero. */
if (cmp_result != 0)
    return cmp_result;

/* Compare the start values for the ranges, if they don't match
   then we're not dealing with equal ranges. */
cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ);
/* Return False or error to the caller. 
   lens are equal, this checks their starting values
       >>> range(0, 10) == range(10, 20)  # False
   Lengths are equal and non-zero, steps don't match.*/
if (cmp_result != 1)
    return cmp_result;

/* Check if the length is equal to 1. 
   If start is the same and length is 1, they represent the same sequence:
       >>> range(0, 10, 10) == range(0, 20, 20)  # True */
one = PyLong_FromLong(1);
if (!one)
    return -1;
cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ);
Py_DECREF(one);
/* Return True or error to the caller. */
if (cmp_result != 0)
    return cmp_result;

/* Finally, just compare their steps */
return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ);

I've also scattered some of my own comments here; look at @ajcr's answer for the Python equivalent.

2 of 5
17

Direct quote from the docs (emphasis mine):

Testing range objects for equality with == and != compares them as sequences. That is, two range objects are considered equal if they represent the same sequence of values. (Note that two range objects that compare equal might have different start, stop and step attributes, for example range(0) == range(2, 1, 3) or range(0, 3, 2) == range(0, 4, 2).)

If you compare ranges with the "same" list, you'll get inequality, as stated in the docs as well:

Objects of different types, except different numeric types, never compare equal.

Example:

>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True

Note that this explicitly only applies to Python 3. In Python 2, where range just returns a list, range(1) == [0] evaluates as True.

🌐
Enki
enki.com › post › how-to-use-python-range-function
Enki | Blog - How to use Python Range Function
When you give one argument, range() starts from zero and goes up to but doesn't include the number you provide. This creates a list of numbers starting at 0 and ending at 4.
Find elsewhere
🌐
Python Central
pythoncentral.io › pythons-range-function-explained
What Is the Range of the Function | Python for Range | Range() Python
January 27, 2022 - range() (and Python in general) is 0-index based, meaning list indexes start at 0, not 1. eg. The syntax to access the first element of a list is mylist[0]. Therefore the last integer generated by range() is up to, but not including, stop.
🌐
Programiz
programiz.com › python-programming › methods › built-in › range
Python range() Function
The Python range() function generates a sequence of numbers. By default, the sequence starts at 0, increments by 1, and stops before the specified number.
🌐
Codingem
codingem.com › home › python range() function: a complete guide (with examples)
Python range() Function: A Complete Guide (with Examples)
November 3, 2022 - Here the range starts from 0 because you did not specify a starting value. The range ends at the value of 5 instead of 6 due to the exclusive nature of range() function. Another way you can call the range() function in Python is by specifying ...
🌐
PYnative
pynative.com › home › python › python range() explained with examples
Python range() Function Explained with Examples
March 17, 2022 - The range() function generates a sequence of integer numbers as per the argument passed. The below steps show how to use the range() function in Python. ... For example, range(0, 6). Here, start=0 and stop = 6. It will generate integers starting ...
🌐
Reddit
reddit.com › r/learnpython › why is range(5) giving me range(0,5) instead of 0 1 2 3 4
Why is range(5) giving me range(0,5) instead of 0 1 2 3 4 : r/learnpython
October 25, 2022 - In Python 2, range(5) would have given you (0,1,2,3,4), but since Python 3, the range is a thing all of its own, and you have to convert it to a list.
Top answer
1 of 6
52

In Python 3.x ,

range(0,3) returns a class of immutable iterable objects that lets you iterate over them, it does not produce lists, and they do not store all the elements in the range in memory, instead they produce the elements on the fly (as you are iterating over them) , whereas list(range(0,3)) produces a list (by iterating over all the elements and appending to the list internally) .

Example -

>>> range(0,3)
range(0, 3)
>>> list(range(0,3))
[0, 1, 2]

Ideally, if you only want to iterate over that range of values , range(0,3) would be faster than (list(range(0,3)) because the latter has the overhead of producing a list before you start iterating over it.

In Python 2.x , range(0,3) produces an list, instead we also had an xrange() function that has similar behavior of range() function from Python 3.x (xrange was renamed to range in Python 3.x)

For Python 3.5, From the documentation -

Range objects implement the collections.abc.Sequence ABC, and provide features such as containment tests, element index lookup, slicing and support for negative indices

So you can do things like -

>>> range(0,10)[5]
5
>>> range(0,10)[3:7]
range(3, 7)
>>> 5 in range(6,10)
False
>>> 7 in range(1,8)
True

And all of these are constant time operations , as can be seen from this test -

In [11]: %timeit a = xrange(0,1000000)[1000]
1000000 loops, best of 3: 342 ns per loop

In [12]: %timeit a = xrange(0,1000000)[10000]
1000000 loops, best of 3: 342 ns per loop

In [13]: %timeit a = xrange(0,1000000)[100000]
1000000 loops, best of 3: 342 ns per loop

In [14]: %timeit a = xrange(0,1000000)[999999]
1000000 loops, best of 3: 342 ns per loop

In [15]: %timeit a = xrange(0,10000000)[9999999]
1000000 loops, best of 3: 339 ns per loop

In [16]: %timeit a = xrange(0,1000000000000)[9999999999]
1000000 loops, best of 3: 341 ns per loop
2 of 6
13

It depends on what version of Python you are using.

In Python 2.x, range() returns a list, so they are equivalent.

In Python 3.x, range() returns an immutable sequence type, you need list(range(0,2)) to get a list.

🌐
Python Land
python.land › home › language deep dives › python range() function: how-to tutorial with examples
Python range() Function: How-To Tutorial With Examples • Python Land
June 27, 2023 - If you want to include 0, you need to set end to -1. Now that you’ve seen range in action, I’ll tell you how ranges work internally to understand them best.
🌐
Learn By Example
learnbyexample.org › python-range-function
Python range() Function - Learn By Example
April 20, 2020 - The range() function generates a sequence of numbers from start (default is 0) up to stop, and increments by step (default is 1).
🌐
Real Python
realpython.com › python-range
Python range(): Represent Numerical Ranges – Real Python
November 24, 2024 - It can be called with one, two, or three integer arguments to specify the start, stop, and step of the sequence. By default, it starts at 0 and increments by 1 until it reaches the stop value.
🌐
Rose-Hulman Institute of Technology
rose-hulman.edu › class › cs › csse120 › VideoFiles › 08.1-RangeExpressions › RangeExpressions.pdf pdf
Python’s range expression Recall that a range expression
Python’s range expression · Recall that a range expression · generates integers that can be · used in a FOR loop, like this: In that example, k takes on the · values 0, 1, 2, ... n-1, as the loop runs. That is: Python allows two other forms of the range expression, for your ·
🌐
Mimo
mimo.org › glossary › python › range-function
Python range() Function [Python Tutorial]
The Python range() function generates a sequence of numbers in a range. By default, range() starts at 0, increments by 1, and stops before a specified number.
🌐
Python Morsels
pythonmorsels.com › range
Python's range() function - Python Morsels
January 14, 2025 - The range function accepts a start ... 10 here instead of going all the way to 11. ... When range is given one argument, it starts at 0, and it stops just before that argument....