range 函数
range() 是 Python 内置函数,用于生成一个等差整数序列。它返回的对象在行为上很像列表,但实际上采用惰性求值策略——只在被迭代时逐个产生数值,而不是一次性把所有数字存入内存。这使得 range(1000000) 几乎不消耗额外空间。
三种调用形式
range() 支持一到三个参数,分别对应不同的使用场景:
一个参数:range(stop)
只给定终止值时,序列从 0 开始,步长为 1,不包含 stop 本身:
for i in range(5):
print(i)
输出 0、1、2、3、4,共 5 个值。range(5) 生成的序列长度为 5,恰好对应长度为 5 的列表的所有合法索引。
两个参数:range(start, stop)
给定起始值和终止值,序列从 start 开始,仍然不包含 stop:
for i in range(3, 8):
print(i)
输出 3、4、5、6、7。这种形式适合需要非零起始索引的场景,比如处理列表的某个切片。
三个参数:range(start, stop, step)
给定起始值、终止值和步长,序列按指定步长递增或递减:
# 步长为 2,生成偶数
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8
# 步长为 3
print(list(range(0, 10, 3))) # [0, 3, 6, 9]
负数步长
当 step 为负数时,range 生成递减序列。此时 start 必须大于 stop,否则序列为空:
# 倒数
for i in range(5, 0, -1):
print(i) # 5, 4, 3, 2, 1
# 从 10 倒数到 1
for i in range(10, 0, -1):
print(i)
# 负数范围
print(list(range(-10, -100, -30))) # [-10, -40, -70]
注意 range(5, 0, -1) 不包含 0,这与正步长时不包含 stop 的规则一致。如果需要包含 0,应写 range(5, -1, -1)。
惰性求值与内存效率
range() 返回的对象不是列表,而是一种特殊的不可变序列类型。它只记录 start、stop 和 step 三个参数,在迭代时才实时计算下一个值:
r = range(1000000)
print(r) # range(0, 1000000)
print(len(r)) # 1000000
print(r[999999]) # 999999,支持索引访问
直接打印 range 对象不会展开所有元素,而是显示其参数定义。这种设计让超大范围的序列几乎不占内存,也支持 len() 和索引访问等操作。
与 list 的区别
range 对象和列表的关键差异在于求值时机和内存占用:
| 特性 | range | list |
|---|---|---|
| 内存占用 | 固定(只存三个整数) | 与元素数量成正比 |
| 求值方式 | 惰性,迭代时计算 | 立即求值,所有元素已存在 |
| 可变性 | 不可变 | 可变 |
| 元素类型 | 仅限整数 | 任意类型 |
如果需要真正的列表(例如修改元素、使用列表方法),可以用 list() 转换:
numbers = list(range(5))
print(numbers) # [0, 1, 2, 3, 4]
但转换会丧失 range 的内存优势,对于大序列应谨慎使用。
常见用法
按索引迭代序列
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
print(i, a[i])
输出:
0 Mary
1 had
2 a
3 little
4 lamb
不过,这种场景更推荐使用 enumerate(),它同时提供索引和元素,代码更简洁:
for i, word in enumerate(a):
print(i, word)
生成数字序列用于计算
total = sum(range(4)) # 0 + 1 + 2 + 3
print(total) # 6
sum() 接受任何可迭代对象,range 的惰性特性让它成为理想的参数。
重复执行 N 次
当不关心循环变量具体值,只需要固定次数时:
for _ in range(3):
print("重要的事说三遍")
用 _ 作为变量名是 Python 惯例,表示"该变量不会被使用"。
边界与空序列
当参数组合无法产生任何元素时,range 返回空序列,不会报错:
print(list(range(5, 5))) # [],start == stop
print(list(range(5, 3))) # [],start > stop 且 step 为正
print(list(range(3, 5, -1))) # [],start < stop 且 step 为负
成员判断
range 对象支持 in 操作符进行成员判断,且效率很高——它通过算术计算而非逐个扫描来判定:
r = range(0, 1000000, 2)
print(500000 in r) # True,O(1) 时间
print(500001 in r) # False
即使范围极大,成员判断也几乎是瞬间完成的。
常见错误
误以为 range 包含终止值:
# 错误预期:输出 1 到 5
for i in range(1, 5):
print(i) # 实际输出 1, 2, 3, 4
range 遵循左闭右开区间 [start, stop),这是 Python 切片和索引系统的统一设计。
步长为零:
range(1, 5, 0) # ValueError: range() arg 3 must not be zero
步长不能为零,否则序列无法推进。
用浮点数做参数:
range(0, 1, 0.1) # TypeError: 'float' object cannot be interpreted as an integer
range 只接受整数参数。如果需要浮点步长,应使用 numpy.arange() 或手动循环。
range() 是 Python 中生成整数序列的标准工具,理解其惰性求值特性和参数规则,能够在保持代码简洁的同时获得出色的内存效率。