Does range() really create lists?
Question:
Both my professor and this guy claim that range
creates a list of values.
“Note: The range function simply returns a list containing the numbers
from x to y-1. For example, range(5, 10) returns the list [5, 6, 7, 8,
9].”
I believe this is to be inaccurate because:
type(range(5, 10))
<class 'range'>
Furthermore, the only apparent way to access the integers created by range
is to iterate through them, which leads me to believe that labeling range
as a lists is incorrect.
Answers:
In Python 2.x, range
returns a list, but in Python 3.x range
returns an immutable sequence, of type range
.
Python 2.x:
>>> type(range(10))
<type 'list'>
>>> type(xrange(10))
<type 'xrange'>
Python 3.x:
>>> type(range(10))
<class 'range'>
In Python 2.x, if you want to get an iterable object, like in Python 3.x, you can use xrange
function, which returns an immutable sequence of type xrange
.
Advantage of xrange
over range
in Python 2.x:
The advantage of xrange()
over range()
is minimal (since xrange()
still has to create the values when asked for them) except when a very large range is used on a memory-starved machine or when all of the range’s elements are never used (such as when the loop is usually terminated with break).
Note:
Furthermore, the only apparent way to access the integers created by range()
is to iterate through them,
Nope. Since range
objects in Python 3 are immutable sequences, they support indexing as well. Quoting from the range
function documentation,
Ranges implement all of the common sequence operations except concatenation and repetition
…
Range objects implement the collections.abc.Sequence
ABC, and provide features such as containment tests, element index lookup, slicing and support for negative indices.
For example,
>>> range(10, 20)[5]
15
>>> range(10, 20)[2:5]
range(12, 15)
>>> list(range(10, 20)[2:5])
[12, 13, 14]
>>> list(range(10, 20, 2))
[10, 12, 14, 16, 18]
>>> 18 in range(10, 20)
True
>>> 100 in range(10, 20)
False
All these are possible with that immutable range
sequence.
Recently, I faced a problem and I think it would be appropriate to include here. Consider this Python 3.x code
from itertools import islice
numbers = range(100)
items = list(islice(numbers, 10))
while items:
items = list(islice(numbers, 10))
print(items)
One would expect this code to print every ten numbers as a list, till 99. But, it would run infinitely. Can you reason why?
Solution
Because the range
returns an immutable sequence, not an iterator object. So, whenever islice
is done on a range
object, it always starts from the beginning. Think of it as a drop-in replacement for an immutable list. Now the question comes, how will you fix it? Its simple, you just have to get an iterator out of it. Simply change
numbers = range(100)
to
numbers = iter(range(100))
Now, numbers
is an iterator object and it remembers how long it has been iterated before. So, when the islice
iterates it, it just starts from the place where it previously ended.
It depends.
In python-2.x, range
actually creates a list (which is also a sequence) whereas xrange
creates an xrange
object that can be used to iterate through the values.
On the other hand, in python-3.x, range
creates an iterable (or more specifically, a sequence)
range creates a list if the python version used is 2.x .
In this scenario range is to be used only if its referenced more than once otherwise use xrange which creates a generator there by redusing the memory usage and sometimes time as it has lazy approach.
xrange is not there in python 3.x rather range stands for what xrange is for python 2.x
refer to question
What is the difference between range and xrange functions in Python 2.X?
var = list(range(start, end+1)
Python3
for
loop with range
function
To understand what for i in range()
means in Python3, we need first to understand the working of the range()
function.
The range()
function uses the generator to produce numbers. It doesn’t generate all numbers at once.
As you know range()
returns the range
object. A range
object uses the same (small) amount of memory, no matter the size of the range it represents. It only stores the start, stop and step values and calculates individual items and subranges as needed.
I.e., It generates the next value only when for
loop iteration asked for it. In each loop iteration, It generates the next value and assigns it to the iterator variable i
.
So it means range()
produces numbers one by one as the loop moves to the next iteration. It saves lots of memory, which makes range() faster and efficient.
Reference: PyNative – for loop with range()
Both my professor and this guy claim that range
creates a list of values.
“Note: The range function simply returns a list containing the numbers
from x to y-1. For example, range(5, 10) returns the list [5, 6, 7, 8,
9].”
I believe this is to be inaccurate because:
type(range(5, 10))
<class 'range'>
Furthermore, the only apparent way to access the integers created by range
is to iterate through them, which leads me to believe that labeling range
as a lists is incorrect.
In Python 2.x, range
returns a list, but in Python 3.x range
returns an immutable sequence, of type range
.
Python 2.x:
>>> type(range(10))
<type 'list'>
>>> type(xrange(10))
<type 'xrange'>
Python 3.x:
>>> type(range(10))
<class 'range'>
In Python 2.x, if you want to get an iterable object, like in Python 3.x, you can use xrange
function, which returns an immutable sequence of type xrange
.
Advantage of xrange
over range
in Python 2.x:
The advantage of
xrange()
overrange()
is minimal (sincexrange()
still has to create the values when asked for them) except when a very large range is used on a memory-starved machine or when all of the range’s elements are never used (such as when the loop is usually terminated with break).
Note:
Furthermore, the only apparent way to access the integers created by
range()
is to iterate through them,
Nope. Since range
objects in Python 3 are immutable sequences, they support indexing as well. Quoting from the range
function documentation,
Ranges implement all of the common sequence operations except concatenation and repetition
…
Range objects implement the
collections.abc.Sequence
ABC, and provide features such as containment tests, element index lookup, slicing and support for negative indices.
For example,
>>> range(10, 20)[5]
15
>>> range(10, 20)[2:5]
range(12, 15)
>>> list(range(10, 20)[2:5])
[12, 13, 14]
>>> list(range(10, 20, 2))
[10, 12, 14, 16, 18]
>>> 18 in range(10, 20)
True
>>> 100 in range(10, 20)
False
All these are possible with that immutable range
sequence.
Recently, I faced a problem and I think it would be appropriate to include here. Consider this Python 3.x code
from itertools import islice
numbers = range(100)
items = list(islice(numbers, 10))
while items:
items = list(islice(numbers, 10))
print(items)
One would expect this code to print every ten numbers as a list, till 99. But, it would run infinitely. Can you reason why?
Solution
Because the
range
returns an immutable sequence, not an iterator object. So, wheneverislice
is done on arange
object, it always starts from the beginning. Think of it as a drop-in replacement for an immutable list. Now the question comes, how will you fix it? Its simple, you just have to get an iterator out of it. Simply change
numbers = range(100)
to
numbers = iter(range(100))
Now,
numbers
is an iterator object and it remembers how long it has been iterated before. So, when theislice
iterates it, it just starts from the place where it previously ended.
It depends.
In python-2.x, range
actually creates a list (which is also a sequence) whereas xrange
creates an xrange
object that can be used to iterate through the values.
On the other hand, in python-3.x, range
creates an iterable (or more specifically, a sequence)
range creates a list if the python version used is 2.x .
In this scenario range is to be used only if its referenced more than once otherwise use xrange which creates a generator there by redusing the memory usage and sometimes time as it has lazy approach.
xrange is not there in python 3.x rather range stands for what xrange is for python 2.x
refer to question
What is the difference between range and xrange functions in Python 2.X?
var = list(range(start, end+1)
Python3
for
loop with range
function
To understand what for i in range()
means in Python3, we need first to understand the working of the range()
function.
The range()
function uses the generator to produce numbers. It doesn’t generate all numbers at once.
As you know range()
returns the range
object. A range
object uses the same (small) amount of memory, no matter the size of the range it represents. It only stores the start, stop and step values and calculates individual items and subranges as needed.
I.e., It generates the next value only when for
loop iteration asked for it. In each loop iteration, It generates the next value and assigns it to the iterator variable i
.
So it means range()
produces numbers one by one as the loop moves to the next iteration. It saves lots of memory, which makes range() faster and efficient.
Reference: PyNative – for loop with range()