## 问题：Python 2.X中的range和xrange函数之间有什么区别？

``````for i in range(0, 20):
for i in xrange(0, 20):
``````

Apparently xrange is faster but I have no idea why it’s faster (and no proof besides the anecdotal so far that it is faster) or what besides that is different about

``````for i in range(0, 20):
for i in xrange(0, 20):
``````

## 回答 0

• `range`创建一个列表，所以如果您这样做`range(1, 10000000)`，则会在内存中创建一个包含`9999999`元素的列表。

• `xrange` 是一个延迟计算的序列对象。

In Python 2.x:

• `range` creates a list, so if you do `range(1, 10000000)` it creates a list in memory with `9999999` elements.

• `xrange` is a sequence object that evaluates lazily.

In Python 3, `range` does the equivalent of python’s `xrange`, and to get the list, you have to use `list(range(...))`.

## 回答 1

range会创建一个列表，因此，如果执行`range(1, 10000000)`此操作，则会在内存中创建一个包含`9999999`元素的列表。

`xrange` 是一个生成器，所以它是一个序列对象，是一个懒惰求值的对象。

``list(range(1,100))``

range creates a list, so if you do `range(1, 10000000)` it creates a list in memory with `9999999` elements.

`xrange` is a generator, so it is a sequence object is a that evaluates lazily.

This is true, but in Python 3, `.range()` will be implemented by the Python 2 `.xrange()`. If you need to actually generate the list, you will need to do:

``````list(range(1,100))
``````

## 回答 2

``````\$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
\$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop``````

Remember, use the `timeit` module to test which of small snippets of code is faster!

``````\$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
\$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop
``````

Personally, I always use `.range()`, unless I were dealing with really huge lists — as you can see, time-wise, for a list of a million entries, the extra overhead is only 0.04 seconds. And as Corey points out, in Python 3.0 `.xrange()` will go away and `.range()` will give you nice iterator behavior anyway.

## 回答 3

`xrange`仅存储范围参数并按需生成数字。但是，Python的C实现当前将其args限制为C long：

``````xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]``````

`xrange` only stores the range params and generates the numbers on demand. However the C implementation of Python currently restricts its args to C longs:

``````xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]
``````

Note that in Python 3.0 there is only `range` and it behaves like the 2.x `xrange` but without the limitations on minimum and maximum end points.

## 回答 4

xrange返回一个迭代器，一次只在内存中保留一个数字。range将整个数字列表保留在内存中。

xrange returns an iterator and only keeps one number in memory at a time. range keeps the entire list of numbers in memory.

## 回答 5

xrange类型的优点在于，无论xrange对象代表的范围大小如何，它始终将占用相同的内存量。没有一致的性能优势。

``````print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)``````

Do spend some time with the Library Reference. The more familiar you are with it, the faster you can find answers to questions like this. Especially important are the first few chapters about builtin objects and types.

The advantage of the xrange type is that an xrange object will always take the same amount of memory, no matter the size of the range it represents. There are no consistent performance advantages.

Another way to find quick information about a Python construct is the docstring and the help-function:

``````print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
``````

## 回答 6

I am shocked nobody read doc:

This function is very similar to `range()`, but returns an `xrange` object instead of a list. This is an opaque sequence type which yields the same values as the corresponding list, without actually storing them all simultaneously. 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`).

## 回答 7

range创建一个列表，因此如果执行range（1，10000000），它将在内存中创建一个包含10000000个元素的列表。xrange是一个生成器，因此它懒惰地求值。

1. 您可以迭代更长的列表而无需获取`MemoryError`
2. 当它懒散地解析每个数字时，如果您尽早停止迭代，您将不会浪费时间创建整个列表。

range creates a list, so if you do range(1, 10000000) it creates a list in memory with 10000000 elements. xrange is a generator, so it evaluates lazily.

This brings you two advantages:

1. You can iterate longer lists without getting a `MemoryError`.
2. As it resolves each number lazily, if you stop iteration early, you won’t waste time creating the whole list.

## 回答 8

``````import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds``````

``````import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 2.78506207466 seconds``````

You will find the advantage of `xrange` over `range` in this simple example:

``````import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds
``````

The above example doesn’t reflect anything substantially better in case of `xrange`.

Now look at the following case where `range` is really really slow, compared to `xrange`.

``````import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 2.78506207466 seconds
``````

With `range`, it already creates a list from 0 to 100000000(time consuming), but `xrange` is a generator and it only generates numbers based on the need, that is, if the iteration continues.

In Python-3, the implementation of the `range` functionality is same as that of `xrange` in Python-2, while they have done away with `xrange` in Python-3

Happy Coding!!

## 回答 9

range（）将从头到尾创建一个值列表（在您的示例中为0 .. 20）。在很大范围内，这将成为昂贵的操作。

It is for optimization reasons.

range() will create a list of values from start to end (0 .. 20 in your example). This will become an expensive operation on very large ranges.

xrange() on the other hand is much more optimised. it will only compute the next value when needed (via an xrange sequence object) and does not create a list of all values like range() does.

## 回答 10

`range(x,y)`如果使用`for`循环，则返回x和y之间的每个数字的列表，这样`range`比较慢。实际上，`range`具有较大的Index范围。`range(x.y)`将打印出x和y之间所有数字的列表

`xrange(x,y)`返回，`xrange(x,y)`但如果使用`for`循环，则`xrange`速度更快。`xrange`索引范围较小。`xrange`不仅会打印出来`xrange(x,y)`，还会保留其中的所有数字。

``````[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)``````

``````[In] for i in range(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9
[In] for i in xrange(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9``````

`range(x,y)` returns a list of each number in between x and y if you use a `for` loop, then `range` is slower. In fact, `range` has a bigger Index range. `range(x.y)` will print out a list of all the numbers in between x and y

`xrange(x,y)` returns `xrange(x,y)` but if you used a `for` loop, then `xrange` is faster. `xrange` has a smaller Index range. `xrange` will not only print out `xrange(x,y)` but it will still keep all the numbers that are in it.

``````[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)
``````

If you use a `for` loop, then it would work

``````[In] for i in range(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9
[In] for i in xrange(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9
``````

There isn’t much difference when using loops, though there is a difference when just printing it!

## 回答 11

range（）： range（1，10）返回一个1到10个数字的列表，并将整个列表保存在内存中。

xrange（）：类似于range（），但不返回列表，而是返回一个对象，该对象根据需要生成范围内的数字。对于循环，这比range（）快一点，并且内存效率更高。xrange（）对象就像一个迭代器，并根据需要生成数字。（延迟评估）

``````In [1]: range(1,10)

Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)

Out[2]: xrange(10)

In [3]: print xrange.__doc__

xrange([start,] stop[, step]) -> xrange object``````

range(): range(1, 10) returns a list from 1 to 10 numbers & hold whole list in memory.

xrange(): Like range(), but instead of returning a list, returns an object that generates the numbers in the range on demand. For looping, this is lightly faster than range() and more memory efficient. xrange() object like an iterator and generates the numbers on demand.(Lazy Evaluation)

``````In [1]: range(1,10)

Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)

Out[2]: xrange(10)

In [3]: print xrange.__doc__

xrange([start,] stop[, step]) -> xrange object
``````

## 回答 12

*事实上，`issubclass(xrange, collections.Sequence)`回报率`True`在2.6-2.7 3.0-3.1和是一个错误是固定在3.2，而不是向后移植。

Some of the other answers mention that Python 3 eliminated 2.x’s `range` and renamed 2.x’s `xrange` to `range`. However, unless you’re using 3.0 or 3.1 (which nobody should be), it’s actually a somewhat different type.

As the 3.1 docs say:

Range objects have very little behavior: they only support indexing, iteration, and the `len` function.

However, in 3.2+, `range` is a full sequence—it supports extended slices, and all of the methods of `collections.abc.Sequence` with the same semantics as a `list`.*

And, at least in CPython and PyPy (the only two 3.2+ implementations that currently exist), it also has constant-time implementations of the `index` and `count` methods and the `in` operator (as long as you only pass it integers). This means writing `123456 in r` is reasonable in 3.2+, while in 2.7 or 3.1 it would be a horrible idea.

* The fact that `issubclass(xrange, collections.Sequence)` returns `True` in 2.6-2.7 and 3.0-3.1 is a bug that was fixed in 3.2 and not backported.

## 回答 13

range（x）返回一个列表，该列表在内存中创建有x个元素。

``````>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]``````

xrange（x）返回一个xrange对象，该对象是生成器obj，可按需生成数字。它们是在for循环（惰性评估）期间计算的。

``````>>> b = xrange(5)
>>> b
xrange(5)``````

In python 2.x

range(x) returns a list, that is created in memory with x elements.

``````>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
``````

xrange(x) returns an xrange object which is a generator obj which generates the numbers on demand. they are computed during for-loop(Lazy Evaluation).

For looping, this is slightly faster than range() and more memory efficient.

``````>>> b = xrange(5)
>>> b
xrange(5)
``````

## 回答 14

``````import time

for x in range(1, 10):

t = time.time()
[v*10 for v in range(1, 10000)]
print "range:  %.4f" % ((time.time()-t)*100)

t = time.time()
[v*10 for v in xrange(1, 10000)]
print "xrange: %.4f" % ((time.time()-t)*100)``````

``````\$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?``````

``````range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509``````

When testing range against xrange in a loop (I know I should use timeit, but this was swiftly hacked up from memory using a simple list comprehension example) I found the following:

``````import time

for x in range(1, 10):

t = time.time()
[v*10 for v in range(1, 10000)]
print "range:  %.4f" % ((time.time()-t)*100)

t = time.time()
[v*10 for v in xrange(1, 10000)]
print "xrange: %.4f" % ((time.time()-t)*100)
``````

which gives:

``````\$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?
``````

Or, using xrange in the for loop:

``````range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509
``````

Is my snippet testing properly? Any comments on the slower instance of xrange? Or a better example :-)

## 回答 15

python中的xrange（）和range（）与用户的工作原理相似，但是区别在于，当我们讨论使用这两个函数分配内存的方式时。

xrange() and range() in python works similarly as for the user , but the difference comes when we are talking about how the memory is allocated in using both the function.

When we are using range() we allocate memory for all the variables it is generating, so it is not recommended to use with larger no. of variables to be generated.

xrange() on the other hand generate only a particular value at a time and can only be used with the for loop to print all the values required.

## 回答 16

range生成整个列表并返回。xrange不会-它会按需生成列表中的数字。

range generates the entire list and returns it. xrange does not — it generates the numbers in the list on demand.

## 回答 17

xrange使用迭代器（动态生成值），range返回一个列表。

xrange uses an iterator (generates values on the fly), range returns a list.

## 回答 18

`range`在运行时返回静态列表。
`xrange`返回一个`object`（在某种情况下，它的作用类似于生成器，尽管肯定不是一个），并在需要时从中生成值。

• `xrange`如果要生成一个巨大范围（例如10亿）的列表，则可以使用该选项，尤其是当您拥有像手机这样的“内存敏感系统”时。
• 使用`range`，如果你想在列表几次迭代。

PS：Python 3.x的`range`功能== Python 2.x的`xrange`功能。

What?
`range` returns a static list at runtime.
`xrange` returns an `object` (which acts like a generator, although it’s certainly not one) from which values are generated as and when required.

When to use which?

• Use `xrange` if you want to generate a list for a gigantic range, say 1 billion, especially when you have a “memory sensitive system” like a cell phone.
• Use `range` if you want to iterate over the list several times.

PS: Python 3.x’s `range` function == Python 2.x’s `xrange` function.

## 回答 19

``````a=0
for i in range(1,100000):
a=a+i``````

``````for i in list(range(1,100000)):
a=a+i``````

Everyone has explained it greatly. But I wanted it to see it for myself. I use python3. So, I opened the resource monitor (in Windows!), and first, executed the following command first:

``````a=0
for i in range(1,100000):
a=a+i
``````

and then checked the change in ‘In Use’ memory. It was insignificant. Then, I ran the following code:

``````for i in list(range(1,100000)):
a=a+i
``````

And it took a big chunk of the memory for use, instantly. And, I was convinced. You can try it for yourself.

If you are using Python 2X, then replace ‘range()’ with ‘xrange()’ in the first code and ‘list(range())’ with ‘range()’.

## 回答 20

Python 2.7.12

``````>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is
slightly faster than range() and more memory efficient.``````

Python 3.5.2

``````>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined``````

From the help docs.

Python 2.7.12

``````>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is
slightly faster than range() and more memory efficient.
``````

Python 3.5.2

``````>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined
``````

Difference is apparent. In Python 2.x, `range` returns a list, `xrange` returns an xrange object which is iterable.

In Python 3.x, `range` becomes `xrange` of Python 2.x, and `xrange` is removed.

## 回答 21

range（）-在内存中创建一个新列表，并将整个0到N个项目（总共N + 1个）打印出来。xrange（）-创建一个迭代器实例，该实例扫描项目并仅将当前遇到的项目保留在内存中，因此始终使用相同数量的内存。

On a requirement for scanning/printing of 0-N items , range and xrange works as follows.

range() – creates a new list in the memory and takes the whole 0 to N items(totally N+1) and prints them. xrange() – creates a iterator instance that scans through the items and keeps only the current encountered item into the memory , hence utilising same amount of memory all the time.

In case the required element is somewhat at the beginning of the list only then it saves a good amount of time and memory.

## 回答 22

Range返回一个列表，xrange返回一个xrange对象，无论范围大小如何，该对象都占用相同的内存，因为在这种情况下，每次迭代仅生成一个元素并且可用，而在使用range的情况下，所有元素一次生成，并且在内存中可用。

Range returns a list while xrange returns an xrange object which takes the same memory irrespective of the range size,as in this case,only one element is generated and available per iteration whereas in case of using range, all the elements are generated at once and are available in the memory.

## 回答 23

``````\$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

\$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop``````

The difference decreases for smaller arguments to `range(..)` / `xrange(..)`:

``````\$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

\$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop
``````

In this case `xrange(100)` is only about 20% more efficient.

## 回答 24

range：-range会立即填充所有内容，这意味着范围的每个数字都会占用内存。

xrange：-xrange类似于生成器，当您想要数字的范围但不希望将它们存储时（例如当您要在for loop.so中使用时），它将出现在图片中，这样可以提高内存效率。

range :-range will populate everything at once.which means every number of the range will occupy the memory.

xrange :-xrange is something like generator ,it will comes into picture when you want the range of numbers but you dont want them to be stored,like when you want to use in for loop.so memory efficient.

## 回答 25

`xrange`真的没有完全完成序列

Additionally, if do `list(xrange(...))` will be equivalent to `range(...)`.

So `list` is slow.

Also `xrange` really doesn’t fully finish the sequence

So that’s why its not a list, it’s a `xrange` object

## 回答 26

Python `3.x`引入了更新的实现`range()`（而更新的实现已经可以`2.x`通过Python 通过`xrange()`功能）。

`range()`漏洞利用一种称为惰性评估的策略较新的实现未在范围内创建大量元素，而是引入了class `range`，这是一个轻量级的对象，代表给定范围内的所需元素，而无需将其显式存储在内存中（这听起来像是生成器，但是惰性求值的概念是不同）。

``````# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>``````

``````# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>``````

in Python `2.x`

This function is essentially the old `range()` function that was available in Python `2.x` and returns an instance of a `list` object that contains the elements in the specified range.

However, this implementation is too inefficient when it comes to initialise a list with a range of numbers. For example, `for i in range(1000000)` would be a very expensive command to execute, both in terms of memory and time usage as it requires the storage of this list into the memory.

in Python `3.x` and `xrange()` in Python `2.x`

Python `3.x` introduced a newer implementation of `range()` (while the newer implementation was already available in Python `2.x` through the `xrange()` function).

The `range()` exploits a strategy known as lazy evaluation. Instead of creating a huge list of elements in range, the newer implementation introduces the class `range`, a lightweight object that represents the required elements in the given range, without storing them explicitly in memory (this might sound like generators but the concept of lazy evaluation is different).

As an example, consider the following:

``````# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>
``````

and

``````# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>
``````

## 回答 27

`range`返回确切的结果：一系列连续的整数，定义的长度以0开头`xrange`。但是，将返回“ xrange object”，其作用类似于迭代器

See this post to find difference between range and xrange:

To quote:

`range` returns exactly what you think: a list of consecutive integers, of a defined length beginning with 0. `xrange`, however, returns an “xrange object”, which acts a great deal like an iterator