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 运行本节的例子。