1.了解for循环
1. 什么是 for 循环?
在许多语言中(如 C、Java),for 循环是基于计数器的:
C语言
int main()
{
for(int i = 0; i < 10 ; i++)
{
printf("%d\n",i);
}
}
但 Python 的 for 完全不同:它是迭代器风格的循环,直接遍历一个可迭代对象中的每个元素,而不需要手动管理索引。
Python
for item in [1, 2, 3, 4, 5]:
print(item)
这种设计的优点:
-
简洁:不需要写索引、边界条件、步长。
-
安全:不会出现"差一错误"(off-by-one error)。
-
通用:可以遍历任何可迭代对象(列表、字符串、字典、文件、生成器等)。
2. 可迭代对象与迭代器
-
可迭代对象 :凡是可以用
for...in直接循环的对象,如列表、元组、字符串、字典、集合、文件对象、range、生成器等。 -
迭代器 :实现了
__next__()方法的对象,可以逐个产生元素。for循环内部会自动将可迭代对象转换成迭代器,然后反复调用__next__()直到捕获StopIteration异常。
你可以手动模拟 for 循环的行为:
fruits = ["苹果", "香蕉", "橙子"]
iterator = iter(fruits) # 获取迭代器
while True:
try:
fruit = next(iterator)
print(fruit)
except StopIteration:
break
3. for 循环的语法结构
for 变量 in 可迭代对象:
循环体代码块
else:
循环正常结束(没有被 break 打断)时执行
-
变量:每次循环被赋值为可迭代对象中的下一个元素。 -
循环体:缩进的代码块,对每个元素执行一次。
-
else子句(可选):仅在循环没有被break终止时执行。通常用于"如果没找到"的场景。for i in range(3):
print(i)输出:0 1 2
2.代码执行顺序
1. 基本执行流程
for i in [10, 20, 30]:
print("开始循环")
print(i)
print("结束本次循环")
print("循环结束")
执行顺序:
将可迭代对象
[10,20,30]转换成迭代器。取出第一个元素
10,赋值给i。执行循环体(
print("开始循环")→print(10)→print("结束本次循环"))。回到迭代器,取出下一个元素
20,赋值给i,再次执行循环体。重复直到迭代器耗尽(抛出
StopIteration)。跳出循环,执行
print("循环结束")。
2. range 对象的惰性求值
range(start, stop, step) 不会一次性生成所有数字,而是返回一个惰性的 range 对象,它只在迭代时逐个生成数字。这节省内存,尤其适合大范围循环。
for i in range(10**9): # 不会占用大量内存
if i == 5:
break
3. 循环中改变可迭代对象的风险
不要在遍历列表的同时修改列表长度(添加或删除元素),这会导致元素被跳过或重复迭代。
lst = [1, 2, 3, 4]
for x in lst:
if x % 2 == 0:
lst.remove(x) # 危险!跳过某些元素
print(lst) # 可能得到 [1, 3] 还是 [1, 3, 4]?取决于实现,不可预测。
正确做法:遍历副本 for x in lst[:]: 或者用列表推导式创建新列表。
4. break、continue、else 的执行顺序
-
break:立即终止整个循环,跳过else子句。 -
continue:跳过本次循环剩余代码,进入下一次迭代。 -
else:仅在循环正常结束(没有遇到break)时执行。for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, '=', x, '*', n//x)
break
else:
print(n, '是素数')
输出会列出素数和合数。这里的 else 属于内层 for,当内层循环没有被 break 时执行。
5. 理解 for 循环对迭代器的隐式调用
for 循环实质上等价于:
iterator = iter(iterable)
while True:
try:
var = next(iterator)
except StopIteration:
break
else:
# 循环体
...
了解这个等价关系有助于理解为什么在循环中对 iterable 进行某些操作(如重新赋值)不会影响迭代器。
3.遍历---多种方式遍历各自数据结构
1. 遍历列表、元组、字符串
colors = ['红', '绿', '蓝']
for c in colors:
print(c)
# 遍历字符串中的字符
for ch in "Python":
print(ch)
2. 使用 enumerate() 同时获取索引和值
当需要知道当前元素的索引时,不要手动维护计数器,用 enumerate:
for idx, fruit in enumerate(['苹果', '香蕉', '橙子']):
print(f"{idx}: {fruit}") # 0: 苹果, 1: 香蕉, 2: 橙子
可以指定起始索引:enumerate(列表, start=1)。
3. 遍历多个列表:zip()
同时遍历两个或多个等长的可迭代对象:
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name} 得分 {score}")
如果长度不一致,zip 会以最短的为准。如果需要以最长为准,可以使用 itertools.zip_longest。
4. 遍历字典
遍历键:
for key in dict:或for key in dict.keys():遍历值:
for value in dict.values():遍历键值对:
for key, value in dict.items():
person = {'name': '张三', 'age': 30, 'city': '北京'}
for k, v in person.items():
print(f"{k} -> {v}")
5. 遍历文件对象
文件对象是可迭代的
with open('file.txt') as f:
for line in f: # 不会一次性读入整个文件
print(line.strip())
这是处理大文件最推荐的方式。
6.使用 range() 进行数字循环
虽然不常用,但你也可以用索引方式遍历列表:
fruits = ['苹果', '香蕉', '橙子']
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
但 enumerate 更优。
7.反向遍历:reversed()
for item in reversed([1, 2, 3]):
print(item) # 3,2,1
8.带条件的遍历:结合 if
可以在循环体内加条件,也可以使用生成器表达式过滤:
# 方法1
for x in range(20):
if x % 2 == 0:
print(x)
# 方法2:先生成偶数列表再遍历
for x in (x for x in range(20) if x % 2 == 0):
print(x)
# 方法3:使用 filter
for x in filter(lambda x: x % 2 == 0, range(20)):
print(x)
4.性能、惯用法与常见陷阱
1. for 循环与 while 循环的选择
-
for适用于遍历已知可迭代对象(元素个数明确或可迭代)。 -
while适用于基于条件循环(不知道何时终止,例如用户输入直到合法)。
2. 列表推导式 vs for 循环
列表推导式是 for 循环的一种高效替代,用于生成新列表。它通常比等价的 for 循环更快,因为内部使用专用 C 级实现。
# 慢
squares = []
for x in range(1000):
squares.append(x**2)
# 快
squares = [x**2 for x in range(1000)]
3. 避免在循环中重复计算不变量
# 不推荐
for i in range(len(data)):
process(i, len(data)) # len(data) 每次都要计算
# 推荐
n = len(data)
for i in range(n):
process(i, n)
4. 使用 itertools 模块实现更复杂的遍历
-
itertools.chain:将多个可迭代对象串联。 -
itertools.product:嵌套循环的笛卡尔积。 -
itertools.combinations/permutations:组合与排列。 -
itertools.cycle:无限循环遍历。
避免多层嵌套循环
from itertools import product
for i, j in product(range(3), range(3)):
print(i, j) # 输出9对
5. 生成器函数与 yield ------ 自定义迭代器
当你需要复杂的遍历逻辑时,可以编写生成器函数:
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
for num in fibonacci(10):
print(num)
6. for 循环的性能特性
-
Python 的
for循环比 C 语言慢,因为每次迭代都要进行对象创建和类型检查。 -
对于数值密集型循环,考虑使用 NumPy 向量化操作。
-
对于纯 Python 循环,尽量将工作移动到内置函数(如
sum,map,filter)或列表推导式中。
7. 常见陷阱总结
| 陷阱 | 后果 | 解决方案 |
|---|---|---|
| 遍历时修改列表长度 | 跳过或重复元素 | 遍历副本 lst[:],或用列表推导式过滤 |
忘记 enumerate,手动维护索引 |
代码冗余,易出错 | 使用 enumerate |
使用 range(len(lst)) 而不是直接遍历 |
不 Pythonic,且慢 | 直接用 for x in lst: 或 enumerate |
| 在循环中调用函数却没有缓存结果 | 重复计算,效率低 | 将不变结果提到循环外 |
误用 else 子句(以为每次都会执行) |
逻辑错误 | 牢记 else 仅在无 break 时执行 |
| 在循环内定义大对象却未复用 | 浪费内存和 CPU | 将定义移到循环外 |
感谢你的观看,期待我们下次再见!