Skip to content

range

range 用来按规则产生一串连续整数,常和 for 循环 一起用。

它包含起始值,不包含结束值。range(0, 10) 是 0 到 9,不是 0 到 10。

为什么需要 range

前面写列表时,数字是一个一个敲进去的:

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

十个数还能忍。如果要 0 到 999,总不能手写一千个数字。更常见的情况是:我只知道“从几开始、到几结束、隔几个取一个”,并不想先把整串数存下来。

range 就是干这件事的。你给出起点、终点和步长,它按这个规则产出整数。

三种写法

只写一个数,表示从 0 开始,到这个数为止(不包含它):

print(list(range(5)))
[0, 1, 2, 3, 4]

range(5) 和 range(0, 5) 是一回事。

写两个数,分别是起点和终点:

a = range(0, 10)
print(a)
print(list(a))

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

写三个数,第三个是步长,表示每隔几个取一个:

print(list(range(0, 10, 2)))
print(list(range(1, 10, 3)))
[0, 2, 4, 6, 8]
[1, 4, 7]

range(1, 10, 3) 从 1 开始,每次加 3:1、4、7。再加 3 是 10,10 已经到了终点,所以不包含。

记法可以收成一句:

写法 含义
range(stop) 从 0 到 stop,不包含 stop
range(start, stop) 从 start 到 stop,不包含 stop,步长为 1
range(start, stop, step) 从 start 出发,每次加 step,不超过 stop

含头不含尾

起点算进去,终点不算。这和列表切片 l[起点:终点] 是同一条规则。

print(list(range(3, 8)))
[3, 4, 5, 6, 7]

有 8,没有 3 到 8 一共六个数这种算法。个数用终点减起点:8 - 3 = 5,所以是五个数。

这个规则看起来少了一个数,用起来反而整齐。列表有 4 个元素时,合法下标是 0、1、2、3,正好是 range(4) 或 range(0, 4),不用再减 1。

range 不是列表

Python 3 里 range(...) 本身不是列表。直接打印看到的是一个 range 对象:

a = range(0, 10)
print(a)
print(type(a))
range(0, 10)
<class 'range'>

要看成列表,用 list() 包一层:list(a)。

它不把每个整数都存进内存,只记住起点、终点和步长。所以 range(10**9) 几乎不占地方,真去 list(range(10**9)) 才会试图造出一个巨大的列表。学习时用 list() 看结果没问题;正式写循环时,直接 for i in range(...),不必先转成列表。

旧教程和 Python 2 代码里常出现 xrange。那时的 range 会立刻做成一个列表,xrange 才是“先记住规则、用到再算”的那个。Python 3 把这件事交给了 range,xrange 这个名字已经去掉了。现在写 xrange(10) 会报 NameError。看到旧代码,把 xrange 改成 range 即可,不必再学一套新写法。

三个参数可以单独读出来:

nums = range(2, 11, 3)
print(nums.start)
print(nums.stop)
print(nums.step)
2
11
3

倒着数

步长写成负数,就从大往小走。同样包含起点,不包含终点。

print(list(range(10, 0, -1)))
print(list(range(10, -1, -2)))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[10, 8, 6, 4, 2, 0]

range(10, 0, -1) 从 10 减到 1,0 不算。如果希望把 0 也打出来,终点要写成 -1:range(10, -1, -1)。

方向必须和步长一致。起点比终点大、步长却是正数,一个数都不会产生:

print(list(range(10, 0)))
print(list(range(5, 5)))
[]
[]

range(10, 0) 的步长默认是 1,往更大的方向走,永远碰不到比 10 还小的终点,所以是空的。range(5, 5) 起点和终点相同,按“不含终点”的规则,也是空的。

长度、下标和成员

range 可以用 len 看会产出多少个数,也可以用下标取出其中某一个,不必先变成列表。

nums = range(0, 10)
print(len(nums))
print(nums[0])
print(nums[3])
print(nums[-1])
10
0
3
9

len(range(0, 10)) 是 10。最后一个数是 9,用 nums[-1] 就能拿到,和列表一样从末尾数。

判断某个整数在不在这串数里,用 in:

print(5 in range(0, 10))
print(10 in range(0, 10))
True
False

10 是终点,不含在内,所以是 False。

和 for 一起用

固定次数的循环,用 range 最直接。下面打印 0、1、2,循环体执行 3 次。for 的写法下一节会细讲。

for i in range(3):
    print(i)
0
1
2

要按下标访问列表时,终点写成列表长度,正好覆盖每一个下标:

names = ["tom", "jerry", "spike"]
for i in range(len(names)):
    print(i, names[i])
0 tom
1 jerry
2 spike

len(names) 是 3,range(3) 给出 0、1、2,不会去访问不存在的 names[3]。

只是为了把元素挨个打印时,直接 for name in names 更短。既要下标又要元素时,下一节的 enumerate 更合适。range(len(...)) 适合“我就是要按下标走一遍”的场合。

常见坑

少算或多算一个。 range(1, 10) 到 9 为止。想要 1 到 10,要写成 range(1, 11)。

倒序忘了写负步长。 range(10, 0) 是空列表,不是 10、9、8……。倒序写成 range(10, 0, -1)。

步长不能是小数,也不能是 0。 range 只产生整数。

print(range(0, 1, 0.5))
TypeError: 'float' object cannot be interpreted as an integer

步长为 0 没有意义,会报错:

print(range(0, 10, 0))
ValueError: range() arg 3 must not be zero

不要把 range 当成已经算好的列表去改。 它不能 append,也不能给某个位置赋新值。需要一份可以修改的整数列表时,再用 list(range(...))。

小结

  • range 按起点、终点、步长产生整数
  • 包含起点,不包含终点
  • range(n) 从 0 到 n-1
  • 正步长往大走,负步长往小走;方向反了就是空序列
  • 它不是列表,用 list() 才能打印成 [...]
  • 和 for 配合,用来做固定次数、或按下标遍历

下一节学习 for 循环。用 python examples/range.py 运行本节的例子。