26

Need to understand the difference between range(0,2) and list(range(0,2)), using python2.7

Both return a list so what exactly is the difference?

cyborg
  • 870
  • 1
  • 15
  • 34

6 Answers6

49

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
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • So in Python 3 is `range(x,y)` something akin to a generator? Just something I've been wondering ever since I first saw this behavior... – Kyrubas Jul 05 '15 at 06:22
  • Intentional for now , because the online interpreter I used for getting the timeit example is Python 2.7 , so I used xrange instead of range . But it should not matter because Python 3's range is Python 2.x's xrange() – Anand S Kumar Jul 06 '15 at 01:06
  • Ok I figured it was something like that. Just wanted to make sure it was intentional. – Scott Jul 06 '15 at 06:38
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.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
3

Both commands return a list in Python 2.x. But in Python 3.x, range() returns an immutable sequence, not a list. It is used for iterating and looping over.

petra
  • 2,642
  • 2
  • 20
  • 12
3

Basically, the difference is that range(0, 2) is a generator function and list(range(0, 2)) is an actual list.

A generator function is used in loops. For example, a generator function of a file will read a very large file line by line.

def gen():
    for line in open("hugefile.csv", "r"):
        yield line #Gives back the line every time it is read, but forgets that line after

for line in gen():
    print(line)

This will print every line without overloading the RAM of the computer because you are only reading one by one in both functions. However, if we do something like

def readEntireFile():
    return [line for line in open("hugefile.csv", "r")] #Python has lazy ways of making lists, this is the same as returning a list with all the lines in the file

for line in readEntireFile():
    print(line)

The second part looks the same, but it is not. Originally, we were looping over every line in the file and moving on to the next line once we were done with it. Over here, Python has a list of ALL the lines :/, imagine doing that with a 10GB file! Your code would crash.

Now, let's go back to range() and list(range())

Doing for x in range(0, 6): makes us go to the next number in the range and completely forget about the previous (Screw grammar).

However, doing for x in list(range(0, 6)): keeps the entire list of numbers in the memory and is the same as doing

numlist = [x for x in range(6)]
for x in numlist:
    print(x)

When you need the entire list of data in your code, use the list method. But, when you only need one piece of data at a time (easiest example, copying a file in chunks), use a generator function to save space. You could copy every 1 million lines of a file using only 54 mb (assuming you don't have insanely long lines). However, if we have a tiny 2kb file, we can just copy that thing without a generator. It's not worth the time and is slower in this case.

rassa45
  • 3,482
  • 1
  • 29
  • 43
1

In python3.x, range has its own type

>>> range(1)
range(0, 1)
>>> type(range(1))
<class 'range'>

So if you want to use range() in a for loop, its fine. However you can't use it purely as a list object. You need to convert it to list to do that.

Python2 Example:

>>> L = range(10)
>>> L[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Python3 Example:

>>> L = range(10)
>>> L[::-1]
range(9, -1, -1)
Chandan Nayak
  • 10,117
  • 5
  • 26
  • 36
  • 1
    You can slice a range - `>>> range(0,3)[1:3] range(1, 3)` – Anand S Kumar Jul 05 '15 at 06:04
  • As @AnandSKumar points out in his answer, python3 `range()` is a sequence type and has the same operations as lists and tuples (except concat and repetition) https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range – Scott Jul 05 '15 at 06:21
  • @Anand, you are right about the slicing that it does work on it. I have updated my answer on what i meant. Thanks! – Chandan Nayak Jul 05 '15 at 06:29
1

Range generates an object of class 'range'

whether it persists or not depends on whether it is assigned

a = range(10)
print(type(a))
print(a[0])
print(type(a[0]))

Output:

<class 'range'>
0
<class 'int'>

The output is functionally an immutable ordered container of integers.

Semantically it is a tuple of integers but for efficiency Python implements this as a separate "generator" class rather than of class tuple.

It is a good example of how, lacking compilation, Python is unable to conceal implementation details and the programmer has to be aware of them.