python中的range和enumerate

range 函数详解

基本功能

range() 用于生成一个不可变的整数序列,常用于 for 循环中控制迭代次数。

它不直接返回列表,而是返回一个 range 对象(可迭代、惰性计算)。

语法

1
2
3
range(stop)
range(start, stop)
range(start, stop, step)
参数 含义
start 起始值(包含),默认为 0
stop 结束值(不包含)
step 步长,默认为 1

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 从 0 到 4
for i in range(5):
print(i) # 输出: 0, 1, 2, 3, 4

# 从 2 到 5(不含)
for i in range(2, 6):
print(i) # 2, 3, 4, 5

# 步长为 2
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8

# 反向遍历
for i in range(5, 0, -1):
print(i) # 5, 4, 3, 2, 1

range 对象的特性

  • 内存高效:不是一次性生成所有数字,而是“惰性计算”,只在需要时生成下一个值。
  • 可迭代但不可变:不能修改元素,也不能赋值。
  • 支持索引和切片(Python 3.2+):
1
2
3
r = range(2, 10, 2)
print(r[1]) # 4
print(r[:2]) # range(2, 6, 2)
  • 支持 in 操作(高效,O(1) 时间复杂度):
1
1000 in range(0, 10000, 2)  # True,很快

转换为列表或元组

如果需要立即得到所有值:

1
2
list(range(5))        # [0, 1, 2, 3, 4]
tuple(range(2, 8)) # (2, 3, 4, 5, 6, 7)

注意:range 本身不是列表,使用 list() 才会真正生成数据。

常见用途

场景 示例
控制循环次数 for i in range(10):
遍历索引 for i in range(len(lst)):
生成等差数列 range(0, 100, 5)
反向遍历 range(len(lst)-1, -1, -1)

常见误区

  • 错误:认为 range 返回列表(实际是对象)
    正确:range(5)<class 'range'> 类型

  • 错误:试图修改 range 元素
    range 是不可变的

  • 错误:用 range(len(...)) 遍历元素(应优先用 for item in lst
    推荐:只有需要索引时才用 range(len(lst)),否则用直接遍历

enumerate 函数详解

基本功能

enumerate() 用于在遍历可迭代对象时,同时获取索引和元素值,避免手动维护计数器。

语法

1
enumerate(iterable, start=0)
参数 含义
iterable 任意可迭代对象(列表、字符串、元组等)
start 起始索引,默认为 0

使用示例

1
2
3
4
5
6
7
8
lst = ['a', 'b', 'c']

for i, value in enumerate(lst):
print(i, value)
# 输出:
# 0 a
# 1 b
# 2 c

自定义起始索引

1
2
3
4
5
6
for i, value in enumerate(lst, start=1):
print(i, value)
# 输出:
# 1 a
# 2 b
# 3 c

遍历字符串

1
2
3
4
5
for i, ch in enumerate("hello"):
print(i, ch)
# 0 h
# 1 e
# ...

enumerate 返回什么

enumerate() 返回一个枚举对象(iterator),每次迭代返回一个 (index, value) 元组。

你可以将其转为列表查看内容:

1
list(enumerate(['x', 'y']))  # [(0, 'x'), (1, 'y')]

底层原理

enumerate 等价于以下生成器函数:

1
2
3
4
5
def my_enumerate(iterable, start=0):
count = start
for item in iterable:
yield count, item
count += 1

所以它是惰性计算的,内存友好。

实际应用场景

场景 1:需要索引和值

1
2
for idx, name in enumerate(names):
print(f"{idx+1}. {name}")

场景 2:查找满足条件的索引

1
2
3
4
for i, x in enumerate(nums):
if x == target:
print(f"Found at index {i}")
break

场景 3:与 zip 结合处理多个列表

1
2
for i, (a, b) in enumerate(zip(list1, list2)):
print(f"{i}: {a} vs {b}")

常见误区

  • 错误:认为 enumerate 是从 1 开始的
    默认从 0 开始,可用 start= 修改

  • 错误:只用于列表
    可用于任何可迭代对象:字符串、元组、字典键、生成器等

  • 错误:在循环中修改 i 能跳过元素
    i 是局部变量,修改不影响循环流程

对比:range 与 enumerate

特性 range(len(lst)) enumerate(lst)
是否需要索引
是否需要元素值 需要 lst[i] 直接获取
代码可读性 一般 更好
性能 略快(纯整数) 稍慢(生成元组)
内存 惰性 惰性
推荐程度 不推荐用于遍历元素 强烈推荐

推荐写法对比

1
2
3
4
5
6
7
# 不推荐
for i in range(len(lst)):
print(i, lst[i])

# 推荐
for i, value in enumerate(lst):
print(i, value)

高级技巧与组合用法

enumerate 与 range 组合使用

如果你想从某个索引开始遍历一段序列:

1
2
3
4
5
6
7
8
9
10
lst = ['a', 'b', 'c', 'd', 'e']
start_idx = 2
end_idx = 5

for i, value in enumerate(lst[start_idx:end_idx], start=start_idx):
print(i, value)
# 输出:
# 2 c
# 3 d
# 4 e

这样索引是原始位置,不是切片后的 0,1,2。

enumerate 配合条件跳过

1
2
3
4
for i, char in enumerate(s):
if char == ' ':
continue # 跳过空格
print(i, char)

获取 enumerate 的长度

enumerate 是迭代器,不支持 len()

1
2
3
4
5
e = enumerate([1,2,3])
# len(e) # 报错

# 必须转为 list 才能知道长度
len(list(enumerate([1,2,3]))) # 3(但消耗了迭代器)

与 itertools 结合(高级)

1
2
3
4
5
from itertools import islice

# 枚举前 5 个元素
for i, x in islice(enumerate(large_iterable), 5):
print(i, x)

性能对比(小数据无差别)

方法 适用场景 性能
for x in lst 只需要值 最快
for i, x in enumerate(lst) 需要索引+值 推荐
for i in range(len(lst)) 需要索引+值 可用但不优雅
for i in range(n) 固定次数循环 必用

对于小数据,性能差异可忽略;对于大循环,enumerate 更安全、可读性更高。

总结:最佳实践

问题 推荐做法
遍历列表并需要索引 for i, x in enumerate(lst)
只需要元素 for x in lst
固定次数循环 for i in range(n)
反向遍历索引 for i in range(len(lst)-1, -1, -1)
从某索引开始枚举 enumerate(lst[start:], start=start)
提取带索引的数据 list(enumerate(…))

一句话口诀记忆

range 造数字,enumerate 配对出(索引, 元素)