Python range函数生成什么序列及基本用法

键盘旁的猫
键盘旁的猫
发布时间:2026-11-12| 14人浏览过
原创

详细解析 Python 中 range 函数生成的不可变整数等差序列类型与惰性计算内存机制,系统讲解单参数、双参数与三参数(含负步长)的基本语法、边界规则与实战技巧。

在 Python 中,range() 函数生成的并不是一个直接在内存中装满数字的实体列表(list),而是一个专门表示不可变整数等差序列的 range 类型对象。它采用惰性计算(按需生成)机制,无论其声明的数值范围是一百还是十亿,在内存中都始终只占用极小且恒定的常数空间(O(1) 空间复杂度)。本文将直接剖析 range 对象的本质机制,并系统拆解其单参数、双参数与三参数(含负步长)的具体语法规则及典型应用场景。
Python range函数生成什么序列及基本用法
概览图:Python range 序列模型特征与三要素参数骨架

一、range 函数究竟生成了什么序列?

初学 Python 时,很多读者因经常在 for i in range(5): 中看到连续数字,容易误认为 range() 返回的是一个包含 [0, 1, 2, 3, 4] 的列表。但如果在 Python 交互环境中直接打印它的返回值,会看到截然不同的结果:

r = range(5)
print(r)
print(type(r))

# 示例输出:
range(0, 5)
<class 'range'>

从输出可以看出,range(5) 返回的是一个独立的 range 类实例。在 Python 3 中,它属于标准的不可变内置序列类型(Immutable Sequence),与 list、tuple 平级。

1. 惰性计算与 O(1) 内存机制

range 对象的核心优势在于惰性求值(Lazy Evaluation)。无论你定义的序列范围多么庞大,对象内部仅仅记录三个整型属性:start(起始值)、stop(终止界限)和 step(步长)。它绝不会一次性在内存中铺开所有整数,而是在迭代器请求下一个值、或者用户通过索引访问某一项时,根据数学公式动态计算对应位置的数值。

我们可以通过 sys.getsizeof() 来直观对比 range 与 list 在内存消耗上的本质差异:

import sys

r = range(0, 1000000)
l = list(r)

print("range 内存占用(字节):", sys.getsizeof(r))
print("list 内存占用(字节):", sys.getsizeof(l))

# 示例输出:
range 内存占用(字节): 48
list 内存占用(字节): 8000056

包含 100 万个数字的 list 需要在物理内存中开辟连续空间并存储全部对象指针,占用约 8MB 内存;而同样覆盖 0 到 100 万的 range 对象仅占用 48 字节。即便把范围扩大到十亿甚至百亿,range 的内存占用依然是 48 字节,绝不会引起内存溢出。

Python range函数生成什么序列及基本用法
图1:range 对象惰性存储机制与 list 内存对比(生成百万级序列)

2. range 具备哪些序列特性?

虽然 range 不预存所有数字,但它完全实现了 Python 的序列抽象基类,具备大部分只读序列操作:

  • 获取长度:可以使用 len(r) 直接获取序列包含的元素总数,复杂度为 O(1)。
  • 下标索引访问:支持正向与负向索引,例如 r[0] 取得首项,r[-1] 取得末项,由内部公式直接寻址。
  • 切片操作:对 range 切片(如 r[2:8:2])得到的结果依然是一个新的 range 对象,同样零开销。
  • 常数级成员检测:执行 x in r 时,Python 3 并不会从头逐个比对,而是使用算术公式 (x - start) % step == 0 结合范围边界在 O(1) 时间内完成判断。
  • 不可变性:不支持原地修改或项赋值。执行 r[0] = 10 将引发 TypeError: 'range' object does not support item assignment。

二、range 函数的三种传参形态与区间推导

range() 函数支持三种不同的调用签名,由参数个数决定起始点与步长增量的取值方式:

range(stop)
range(start, stop)
range(start, stop, step)

无论是哪一种调用方式,都严格遵循左闭右开区间 [start, stop) 的规则:包含起始值 start,但在达到或超过终止值 stop 之前停止,即绝对不包含 stop 本身。

Python range函数生成什么序列及基本用法
图2:range 函数三种参数形态的解析与推导推导逻辑

形态一:单参数 range(stop)

当仅传入一个参数时,该参数被视作 stop。此时起始值 start 自动默认为 0,步长 step 自动默认为 1。生成的序列区间为 [0, stop),共包含 stop 个整数。

for num in range(5):
    print(num, end=" ")

# 示例输出:
0 1 2 3 4 
边界注意:如果传入的 stop <= 0,例如 range(0) 或 range(-3),函数不会报错,而是生成一个长度为 0 的空序列。

形态二:双参数 range(start, stop)

当传入两个参数时,第一个参数指定序列的起始数值 start,第二个参数指定终止界限 stop,步长 step 依然默认为 1。生成的区间为 [start, stop)。

for num in range(3, 8):
    print(num, end=" ")

# 示例输出:
3 4 5 6 7 

如果传入的起始值大于或等于终止值(例如 range(5, 5) 或 range(8, 3)),由于默认步长向正方向移动,无法到达终止条件,将生成一个空序列。

形态三:三参数 range(start, stop, step)

当传入三个参数时,第三个参数显式指定每次递进的步长 step。序列中的第 k 项满足 x_k = start + k * step,直至数值达到或跨越 stop 为止。

# 生成 1 到 10 之间的所有奇数
for num in range(1, 10, 2):
    print(num, end=" ")

# 示例输出:
1 3 5 7 9 
语法红线:第三个参数 step 必须为非零整数。如果传入 step=0,Python 会直接抛出 ValueError: range() arg 3 must not be zero。

三、正向递增与逆向递减:步长与边界陷阱

当需要递减遍历或倒数计数时,必须使用三参数形式并传入负数步长(step < 0)。在负步长下,序列的运行方向从左向右变为从右向左,边界规则也随之转变。

Python range函数生成什么序列及基本用法
图3:正负步长下的区间方向、轴线离散点与边界排除对照

1. 负步长的运行条件与边界

使用负步长时,必须满足 start > stop,否则生成的序列为空。更关键的是,“不包含 stop” 的规则在递减时依然完全生效:

# 需求:从 5 倒数到 1
for i in range(5, 0, -1):
    print(i, end=" ")

# 示例输出:
5 4 3 2 1 

2. 倒数至 0 的经典边界陷阱

很多开发者容易犯的一个错误是:想要倒数到 0 时,习惯性把 stop 写成了 0。

正如上面的代码所示,range(5, 0, -1) 的终止值是 0,按照排除规则,序列到达 1 就已经停下,数字 0 根本不会被打印。如果你需要让倒数包含 0,stop 必须设定为 -1:

# 正确包含 0 的写法:stop 设置为 -1
for i in range(5, -1, -1):
    print(i, end=" ")

# 示例输出:
5 4 3 2 1 0 
调用形态与参数 步长方向 有效区间产生条件 最终生成的数字序列
range(4) +1 (默认) stop > 0 0, 1, 2, 3
range(2, 6) +1 (默认) start < stop 2, 3, 4, 5
range(0, 10, 3) +3 (正步长) start < stop 0, 3, 6, 9
range(6, 2, -1) -1 (负步长) start > stop 6, 5, 4, 3
range(5, 1) +1 (默认) start > stop(无解) 空序列(len=0)
range(2, 6, -1) -1 (负步长) start < stop(无解) 空序列(len=0)

四、实际开发中的常见场景与写法辨析

1. 固定次数循环(执行 N 次)

当仅仅需要重复执行某段逻辑指定次数、而不需要关心当前数字时,通常配合匿名变量 _ 使用 range(n):

def retry_connect(attempts=3):
    for i in range(attempts):
        print(f"正在进行第 {i + 1} 次重试...")
        # 执行网络连接逻辑

2. 遍历序列:range(len(...)) 还是 enumerate()?

在许多传统语言背景的开发者代码中,经常能看到利用 range(len(items)) 通过下标索引遍历列表的写法:

fruits = ["苹果", "香蕉", "橙子"]

# 传统写法(不推荐):
for i in range(len(fruits)):
    print(i, fruits[i])

# Pythonic 推荐写法:
for i, fruit in enumerate(fruits):
    print(i, fruit)

辨析建议:如果遍历过程中既需要索引又需要元素本身,推荐优先使用内置的 enumerate() 函数。它在语义上更直观,执行效率也比每次通过下标回查列表更加高效。只有在需要同时跳跃访问、反向定位或需要同步修改原列表特定切片时,才考虑显式使用 range 生成索引。

3. 需要实体数据容器时的显式转换

如果后续逻辑必须使用列表方法(如 .append()、.pop() 或原地排序 .sort()),或者需要将数字序列序列化为 JSON,可以通过构造函数直接将其转换为 list 或 tuple:

nums_list = list(range(1, 6))
nums_tuple = tuple(range(1, 6))

print(nums_list)
print(nums_tuple)

# 示例输出:
[1, 2, 3, 4, 5]
(1, 2, 3, 4, 5)

五、核心要点总结与避坑清单

为了在实际编码中避免低级错误,可以将 range 的核心特性与边界约束整理为如下自查清单:

Python range函数生成什么序列及基本用法
图4:Python range 核心特性、经典场景与避坑决策全景图
  1. 参数只认整数:range() 的所有参数必须是整型(int),传入浮点数(如 range(0.5, 3.5))会直接引发 TypeError: 'float' object cannot be interpreted as an integer。如果需要生成浮点数等差数列,建议使用标准库生成器或第三方科学计算库(如 numpy.arange / numpy.linspace)。
  2. 牢记左闭右开:终止界限 stop 永远不被包含在序列内。凡是需要生成包含上限 N 的序列,stop 都必须设定为 N + 1。
  3. 负步长别忘终点多减 1:逆向递减数到 0 时,stop 必须是 -1;数到 1 时,stop 是 0。
  4. 避免过早实体化:在大数据量或大规模迭代循环中,直接遍历 range 对象即可,切勿随手加一层 list(range(...)),以免无谓消耗系统内存。
相关专题:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。