Python range()
函数终极入门指南:从零开始掌握数字序列的生成
欢迎来到 Python 的世界!作为一门以简洁、易读著称的编程语言,Python 提供了许多强大的内置工具,帮助我们高效地完成各种任务。在众多基础且重要的功能中,range()
函数无疑是每个初学者都必须掌握的核心内容之一。无论你是需要重复执行一段代码特定次数,还是需要生成一个数字序列用于索引或其他计算,range()
都会是你得力的助手。
本篇教程将带你深入浅出地探索 range()
函数的方方面面,从最基础的概念、不同的使用方式,到其背后的工作原理和实际应用场景,力求让你彻底理解并能熟练运用它。即使你没有任何编程基础,跟随本教程的步伐,也能轻松入门。
一、 什么是 range()
函数?
在编程中,我们经常需要处理一系列连续的数字。例如,你可能想打印从 0 到 9 的数字,或者执行某个操作 5 次。手动写出 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
或者重复写五次代码显然不够优雅,尤其当数字范围很大或者重复次数很多时。
Python 的 range()
函数正是为了解决这类问题而设计的。它是一个内置函数,主要功能是生成一个不可变的整数序列。
但这里有一个非常关键的点需要初学者注意:range()
函数并不会直接创建一个包含所有数字的列表(List)。相反,它返回的是一个特殊的 range
对象。这个 range
对象是一个可迭代对象(iterable),意味着你可以遍历它(比如在 for
循环中),但它并不会在内存中一次性存储所有的数字。它只存储了序列的起始值、结束值和步长(间隔),并在需要时动态地计算出下一个数字。
这种“惰性计算”或“按需生成”的特性使得 range()
在处理非常大的数字序列时极为内存高效。想象一下,如果需要一个从 0 到 10 亿的序列,如果 range()
直接生成一个包含 10 亿个整数的列表,那将消耗巨大的内存资源。而 range
对象本身只占用极小的固定内存。
我们可以通过 type()
函数来验证 range()
返回的是什么:
python
sequence = range(5)
print(sequence)
print(type(sequence))
输出会是:
range(0, 5)
<class 'range'>
你看,它打印的是 range(0, 5)
而不是 [0, 1, 2, 3, 4]
,并且类型是 <class 'range'>
。
那么,如果我想看到 range
对象代表的实际数字序列怎么办?最常用的方法是将它转换成列表或在 for
循环中使用。
“`python
方法一:转换为列表(List)
sequence_obj = range(5)
number_list = list(sequence_obj)
print(number_list) # 输出: [0, 1, 2, 3, 4]
方法二:在 for 循环中使用(这是最常见的用法)
print(“Using range in a for loop:”)
for number in range(5):
print(number)
输出:
Using range in a for loop:
0
1
2
3
4
“`
通过这两个例子,我们对 range()
有了初步的认识:它生成数字序列的概念,返回的是一个 range
对象,并且非常适合在 for
循环中使用。
二、 range()
函数的三种形式
range()
函数非常灵活,它支持三种不同的调用方式(也称为“签名”或“重载”),以满足不同的序列生成需求。
形式一:range(stop)
这是最简单的形式,只接受一个参数 stop
。
- 参数
stop
: 指定序列的结束值,但请极其注意:生成的序列不包含这个stop
值本身。也就是说,序列会一直生成到stop - 1
为止。 - 隐含的起始值
start
: 在这种形式下,序列的起始值默认为 0。 - 隐含的步长
step
: 序列中数字之间的间隔(步长)默认为 1。
示例:
“`python
生成从 0 到 4 (不包括 5) 的序列
for i in range(5):
print(f”Current number: {i}”)
如果想看到列表形式
numbers = list(range(5))
print(f”List generated by range(5): {numbers}”) # 输出: [0, 1, 2, 3, 4]
另一个例子
for count in range(3):
print(“Executing iteration”, count + 1)
numbers_zero = list(range(0)) # stop 为 0 或负数,且 start 默认为 0,将生成空序列
print(f”List generated by range(0): {numbers_zero}”) # 输出: []
“`
关键点回顾 (range(stop)
):
- 从 0 开始。
- 到
stop - 1
结束。 - 步长为 1。
- 如果
stop
<= 0,则生成空序列。
形式二:range(start, stop)
这种形式接受两个参数,允许你指定序列的起始值。
- 参数
start
: 指定序列的起始值。生成的序列将包含这个start
值。 - 参数
stop
: 指定序列的结束值。和形式一一样,生成的序列不包含这个stop
值。 - 隐含的步长
step
: 步长默认为 1。
示例:
“`python
生成从 2 到 6 (不包括 7) 的序列
for i in range(2, 7):
print(f”Number: {i}”)
列表形式
numbers = list(range(2, 7))
print(f”List generated by range(2, 7): {numbers}”) # 输出: [2, 3, 4, 5, 6]
生成从 -3 到 1 (不包括 2) 的序列
neg_numbers = list(range(-3, 2))
print(f”List generated by range(-3, 2): {neg_numbers}”) # 输出: [-3, -2, -1, 0, 1]
如果 start >= stop,且步长默认为正数 1,则生成空序列
empty_seq = list(range(5, 2))
print(f”List generated by range(5, 2): {empty_seq}”) # 输出: []
“`
关键点回顾 (range(start, stop)
):
- 从
start
开始(包含start
)。 - 到
stop - 1
结束。 - 步长为 1。
- 如果
start >= stop
,则生成空序列。
形式三:range(start, stop, step)
这是最完整的形式,允许你指定起始值、结束值以及步长。
- 参数
start
: 序列的起始值(包含)。 - 参数
stop
: 序列的结束值(不包含)。 - 参数
step
: 序列中数字之间的间隔。它可以是正数(递增序列),也可以是负数(递减序列)。step
不能为 0,否则会引发ValueError
异常。
示例(正数步长):
“`python
生成从 1 到 9,步长为 2 的序列 (奇数)
for i in range(1, 10, 2):
print(f”Odd number: {i}”)
列表形式
odd_numbers = list(range(1, 10, 2))
print(f”List generated by range(1, 10, 2): {odd_numbers}”) # 输出: [1, 3, 5, 7, 9]
生成从 0 到 14,步长为 3 的序列
step_numbers = list(range(0, 15, 3))
print(f”List generated by range(0, 15, 3): {step_numbers}”) # 输出: [0, 3, 6, 9, 12]
“`
示例(负数步长):
当 step
为负数时,表示生成一个递减的序列。这时,start
值必须大于 stop
值,序列才会包含元素。
“`python
生成从 10 递减到 1 (不包括 0) 的序列
for i in range(10, 0, -1):
print(f”Counting down: {i}”)
列表形式
countdown = list(range(10, 0, -1))
print(f”List generated by range(10, 0, -1): {countdown}”) # 输出: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
生成从 5 递减到 -4,步长为 -2 的序列
neg_step_numbers = list(range(5, -5, -2))
print(f”List generated by range(5, -5, -2): {neg_step_numbers}”) # 输出: [5, 3, 1, -1, -3]
如果 step 为负,但 start <= stop,则生成空序列
empty_neg_step = list(range(1, 10, -1))
print(f”List generated by range(1, 10, -1): {empty_neg_step}”) # 输出: []
“`
示例(步长为 0 – 错误情况):
python
try:
invalid_range = range(1, 10, 0)
except ValueError as e:
print(f"Error occurred: {e}") # 输出: Error occurred: range() arg 3 must not be zero
关键点回顾 (range(start, stop, step)
):
- 从
start
开始(包含start
)。 - 结束条件依赖于
step
的符号:- 如果
step
> 0,序列持续到最后一个小于stop
的值。 - 如果
step
< 0,序列持续到最后一个大于stop
的值。
- 如果
step
决定了序列中数字的间隔和方向。step
不能为 0。- 如果
step
> 0 且start >= stop
,序列为空。 - 如果
step
< 0 且start <= stop
,序列为空。
三、 为什么推荐使用 range()
?(核心优势)
我们已经提到了 range()
的一些优点,现在让我们系统地总结一下:
-
内存效率 (Memory Efficiency): 这是
range()
最显著的优点。range
对象只存储start
,stop
,step
三个值,无论序列本身有多长(哪怕是数百万、数十亿个数字),range
对象占用的内存都是固定的、非常小的。这与直接创建一个包含所有数字的列表形成鲜明对比,后者会随着序列长度线性增加内存消耗。在处理大数据或需要大量迭代时,range()
的内存优势至关重要。“`python
import sys一个包含 1 亿个数字的 range 对象
large_range = range(100_000_000)
print(f”Memory size of range object: {sys.getsizeof(large_range)} bytes”)尝试创建一个包含 1 亿个数字的列表 (这可能会消耗大量内存,甚至导致程序崩溃)
注意:请谨慎运行以下代码,特别是内存较小的机器
try:
large_list = list(range(100_000_000))
print(f”Memory size of list with 100M integers: {sys.getsizeof(large_list)} bytes”)
except MemoryError:
print(“Creating a list with 100 million integers caused a MemoryError!”)
通常 range 对象的大小非常小,而列表的大小会非常大
“`
-
性能 (Performance): 由于
range
对象是按需生成数字的(惰性求值),它在创建时几乎不耗时。只有在迭代过程中,每次需要下一个数字时,才会进行简单的计算。对于只需要遍历一次序列的场景(如for
循环),这通常比先创建一个完整的列表再遍历要快。 -
简洁与可读性 (Conciseness and Readability): 使用
range()
控制for
循环是 Python 中非常惯用和清晰的做法。for i in range(10):
明确地表达了“循环执行 10 次”的意图,比手动维护计数器变量更简洁、更不易出错。 -
灵活性 (Flexibility): 三种形式的
range()
提供了足够的灵活性来生成各种等差整数序列,包括正向、反向、以及带有特定步长的序列。
四、 range()
与列表 (List) 的对比
虽然我们经常使用 list(range(...))
来查看 range
对象的内容,但理解 range
对象和列表之间的本质区别非常重要:
特性 | range 对象 |
列表 (List) |
---|---|---|
类型 | range |
list |
内存使用 | 极小,固定(只存 start, stop, step) | 随元素数量线性增长 |
存储方式 | 不存储所有元素,按需计算 | 存储所有元素在内存中 |
可变性 | 不可变 (Immutable) | 可变 (Mutable) – 可以增删改元素 |
创建时间 | 极快 | 可能较慢(取决于元素数量) |
元素类型 | 只能是整数 (Integers) | 可以包含任何类型的元素 |
主要用途 | 控制循环次数,生成数字序列 | 存储和操作任意数据集合 |
索引和切片 | 支持索引 ([] ) 和切片 ([:] ) |
支持索引 ([] ) 和切片 ([:] ) |
什么时候用 range()
?
- 当你想在
for
循环中迭代固定次数时。 - 当你需要一个整数序列,但不需要立即将所有数字存储在内存中时(特别是序列很长时)。
- 当你需要按特定步长(正或负)生成数字序列时。
- 当你需要基于索引访问一个序列(如列表或字符串)的元素时(虽然
enumerate()
通常是更 Pythonic 的方式,但range(len(sequence))
也很常见)。
什么时候用列表?
- 当你需要存储一个包含不同类型元素(不只是整数)的集合时。
- 当你需要频繁地添加、删除或修改集合中的元素时(因为列表是可变的)。
- 当你需要存储一个相对较小的、需要被多次随机访问或修改的数字序列时。
- 当函数或方法明确要求一个列表作为输入时。
五、 range()
的常见应用场景
-
控制
for
循环执行次数:
这是range()
最经典、最核心的用途。“`python
执行任务 5 次
num_tasks = 5
for i in range(num_tasks):
print(f”Performing task {i + 1}”)模拟进度条
total_steps = 10
print(“Processing:”)
for step in range(total_steps):
# 做一些工作…
print(f”Progress: {((step + 1) / total_steps) * 100:.0f}%”)
print(“Done!”)
“` -
生成数字序列用于计算或初始化:
“`python
计算 1 到 100 的平方和
sum_of_squares = 0
for num in range(1, 101): # 注意是 101,因为 range 不包含 stop
sum_of_squares += num * num
print(f”Sum of squares from 1 to 100: {sum_of_squares}”)使用列表推导式更简洁地创建列表
even_numbers = [x for x in range(0, 21, 2)]
print(f”Even numbers up to 20: {even_numbers}”)squares = [i**2 for i in range(10)]
print(f”Squares of first 10 non-negative integers: {squares}”)
“` -
遍历序列的索引:
虽然直接迭代元素通常更好,但有时确实需要索引。“`python
my_list = [‘apple’, ‘banana’, ‘cherry’, ‘date’]使用 range(len()) 访问索引和元素
print(“\nAccessing list elements by index using range(len()):”)
for i in range(len(my_list)):
print(f”Index {i}: {my_list[i]}”)更 Pythonic 的方式:使用 enumerate()
print(“\nAccessing list elements and indices using enumerate():”)
for index, value in enumerate(my_list):
print(f”Index {index}: {value}”)enumerate 通常是处理索引和值的首选方法,因为它更清晰易读。
“`
-
结合切片(虽然不常用):
range
对象也支持切片,返回一个新的range
对象。“`python
r = range(0, 20, 2) # 0, 2, 4, …, 18
print(f”Original range: {list(r)}”)获取前 5 个元素 (对应索引 0 到 4)
sub_range1 = r[0:5]
print(f”Slice r[0:5]: {list(sub_range1)}”) # 输出: [0, 2, 4, 6, 8]
print(f”Type of slice: {type(sub_range1)}”) # 输出:获取从索引 2 开始到结尾的元素
sub_range2 = r[2:]
print(f”Slice r[2:]: {list(sub_range2)}”) # 输出: [4, 6, 8, 10, 12, 14, 16, 18]
“`
六、 重要注意事项与常见陷阱
-
stop
值不包含: 再次强调,range(start, stop)
或range(stop)
生成的序列最多只到stop - 1
。这是初学者最容易犯错的地方之一。如果你需要包含stop
值本身,你应该使用range(start, stop + 1)
(当步长为正时)。 -
step
不能为零:range(start, stop, 0)
会导致ValueError
。 -
负数步长: 使用负数步长时,确保
start > stop
。否则,range()
会生成一个空序列。 -
只支持整数:
range()
的所有参数(start
,stop
,step
)都必须是整数。你不能直接使用浮点数。如果需要浮点数序列,可以考虑:- 使用
numpy.arange()
或numpy.linspace()
(需要安装 NumPy 库)。 - 自己编写生成器函数或使用列表推导式进行计算。
“`python
错误示例:
range(0.5, 5.5, 0.5) # TypeError: ‘float’ object cannot be interpreted as an integer
使用列表推导式生成近似的浮点数序列
float_seq = [i * 0.5 for i in range(int(0.5 / 0.5), int(5.5 / 0.5))] # 需要一些计算
print(f”Approximate float sequence: {float_seq}”)或者更简单的(如果知道整数范围)
float_seq_simple = [i / 2 for i in range(1, 11)] # 生成 0.5, 1.0, …, 5.0
print(f”Simpler float sequence: {float_seq_simple}”)
“` - 使用
-
range
对象是可迭代的,但不是迭代器: 这稍微有些技术性,但意味着你可以多次遍历同一个range
对象。而迭代器(Iterator)通常只能遍历一次。“`python
my_range = range(3)print(“First iteration:”)
for i in my_range:
print(i)print(“\nSecond iteration (possible with range object):”)
for i in my_range:
print(i)对比迭代器 (例如,通过 iter() 创建)
my_iterator = iter(my_range)
print(“\nIteration using an iterator (first time):”)
for i in my_iterator:
print(i)print(“\nIteration using the same iterator (second time – produces nothing):”)
迭代器已经耗尽,再次遍历不会产生任何结果
for i in my_iterator:
print(i)
print(“(Iterator is exhausted)”)
“`
七、 总结
Python 的 range()
函数是一个基础但极其有用的工具,主要用于生成不可变的整数序列。它返回一个内存高效的 range
对象,该对象按需计算序列中的数字,而不是一次性存储所有数字。
我们学习了 range()
的三种形式:
range(stop)
: 从 0 开始,到stop-1
结束,步长为 1。range(start, stop)
: 从start
开始,到stop-1
结束,步长为 1。range(start, stop, step)
: 从start
开始,根据step
递增或递减,直到(但不包括)stop
。
range()
的核心优势在于其内存效率和简洁性,特别是在控制 for
循环和处理大规模数字序列时。虽然它与列表在概念上相关(都可以表示序列),但它们在内存使用、可变性和用途上有本质区别。
掌握 range()
的用法,理解其工作原理(尤其是 stop
参数的排他性和 range
对象的惰性计算特性),是每一位 Python 学习者打好基础的关键一步。通过不断练习,在实际场景中应用 range()
,你会越来越体会到它的便捷和强大。
希望这篇详细的教程能帮助你彻底理解 Python 的 range()
函数,并在你的编程之旅中灵活运用它!继续探索,享受编程的乐趣!