58

I understand how functions like range() and zip() can be used in a for loop. However I expected range() to output a list - much like seq in the unix shell. If I run the following code:

a=range(10)
print(a)

The output is range(10), suggesting it's not a list but a different type of object. zip() has a similar behaviour when printed, outputting something like

<zip object at "hexadecimal number">

So my question is what are they, what advantages are there to making them this, and how can I get their output to lists without looping over them?

wim
  • 266,989
  • 79
  • 484
  • 630
Michal
  • 1,190
  • 2
  • 12
  • 21
  • 1
    Looks like Python 3. Use `a = list(range(10))`. – Matthias Nov 04 '13 at 21:37
  • 2
    In addition to the good answers here, I recommend reading the answer to [The Python yield keyword explained](http://stackoverflow.com/a/231855/1599111) to learn about generators - an excellent example for why your functions might not want to return a list... just yet. – Lukas Graf Nov 04 '13 at 21:51

3 Answers3

90

You must be using Python 3.

In Python 2, the objects zip and range did behave as you were suggesting, returning lists. They were changed to generator-like objects which produce the elements on demand rather than expand an entire list into memory. One advantage was greater efficiency in their typical use-cases (e.g. iterating over them).

The "lazy" versions also exist in Python 2.x, but they have different names i.e. xrange and itertools.izip.

To retrieve all the output at once into a familiar list object, you may simply call list to iterate and consume the contents:

>>> list(range(3))
[0, 1, 2]
>>> list(zip(range(3), 'abc'))
[(0, 'a'), (1, 'b'), (2, 'c')]
wim
  • 266,989
  • 79
  • 484
  • 630
  • 1
    While these objects are *lazy* I think it is mis-leading to describe them as like generators, e.g. see http://stackoverflow.com/questions/13092267/if-range-is-a-generator-in-python-3-3-why-can-i-not-call-next-on-a-range – Chris_Rands Apr 05 '17 at 13:28
  • 1
    @Chris_Rands They are like generators in the most important sense, that elements are produced on demand. I think my description is fair; I was careful not to actually call them generators, and the differences are not important for this question. – wim Apr 05 '17 at 15:25
  • True, perhaps the tone of my first comment was unfair, sorry, still I'll leave it there because the link provides some further details on range objects for those that are interested – Chris_Rands Apr 05 '17 at 16:35
  • zip is an iterator: >>> z = zip((1,2),(3,4)) >>> next(z) (1, 3) – Dmitriy Sintsov May 30 '18 at 13:39
24

In Python 3.x., range returns a range object instead of a list like it did in Python 2.x. Similarly, zip now returns a zip object instead of a list.

To get these objects as lists, put them in list:

>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> zip('abc', 'abc')
<zip object at 0x01DB7120>
>>> list(zip('abc', 'abc'))
[('a', 'a'), ('b', 'b'), ('c', 'c')]
>>>

While it may seem unhelpful at first, this change in the behavior of range and zip actually increases efficiency. This is because the zip and range objects produce items as they are needed, instead of creating a list to hold them all at once. Doing so saves on memory consumption and improves operation speed.

5

Range (xrange in python 2.*) objects are immutable sequences, while zip (itertools.izip repectively) is a generator object. To make a list from a generator or a sequence, simply cast to list. For example:

>>> x = range(10) 
>>> list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

But they differ in a way how elements are generated. Normal generators are mutable and exaust their content if iterated, while range is immutable, and don't:

>>> list(x) # x is a range-object
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # second cast to list, contents is the same
>>> y = zip('abc', 'abc')
>>> list(y) # y is a generator
[('a', 'a'), ('b', 'b'), ('c', 'c')]
>>> list(y) 
[] # second cast to list, content is empty!
alko
  • 39,930
  • 9
  • 90
  • 97
  • I was playing with these examples at the REPL, following earlier answers to this question. When I first consumed my generator-not-generator produced by zip, I was a bit :facepalm: until I read your mention that the second cast to list would result in empty content. Thank you for this! – wonderfulthunk May 07 '20 at 18:58