Python 的 for 循环与 C/Java 的计数循环不同,它直接遍历可迭代对象中的每一个元素,底层基于迭代器协议:调用 iter() 获取迭代器,反复调用 next() 直到 StopIteration。这使得 for 循环天然适合处理列表、元组、字典、集合、字符串等容器类型。本文将汇总 for 循环遍历各种数据结构的实用技巧,并给出常见坑点和实战案例。
- # for循环基本语法
- for 变量 in 可迭代对象:
- 循环体
- else: # 可选,循环正常结束时执行
- else代码块
- # 示例:遍历列表
- fruits = ["苹果", "香蕉", "橘子"]
- for fruit in fruits:
- print(f"我喜欢吃{fruit}")
- # 字符串也是可迭代对象
- for char in "Python":
- print(char, end=' ') # 输出: P y t h o n
- # 底层机制模拟
- iterator = iter(fruits)
- while True:
- try:
- fruit = next(iterator)
- print(fruit)
- except StopIteration:
- break
复制代码
一、遍历列表
直接遍历最常用。需要索引时推荐用 enumerate(),而非 range(len())。注意:遍历时不要直接修改列表长度(增删元素),否则会导致漏删或无限循环。正确做法是遍历副本、用列表推导式创建新列表,或者反向遍历按索引删除。
- fruits = ["苹果", "香蕉", "橘子", "葡萄", "西瓜"]
- # 基础遍历
- for fruit in fruits:
- print(fruit)
- # 带索引遍历(推荐)
- for i, fruit in enumerate(fruits, start=1):
- print(f"第{i}个: {fruit}")
- # 遍历时删除的正确方式:遍历副本
- numbers = [1, 2, 3, 4, 5]
- for n in numbers[:]: # 副本
- if n % 2 == 0:
- numbers.remove(n)
- # 或列表推导式
- numbers = [n for n in numbers if n % 2 != 0]
- # 或反向遍历删除
- for i in range(len(numbers)-1, -1, -1):
- if numbers[i] % 2 == 0:
- del numbers[i]
复制代码
二、遍历元组
与列表语法相同,但元组不可修改,遍历时不存在意外修改的问题,具有“安全优势”。当元组内包含子元组时,可直接在 for 中解包。
- colors = ("红色", "绿色", "蓝色", "黄色")
- for color in colors:
- print(color)
- # 解包便利:一个学生元组包含姓名、数学成绩、英语成绩
- students = [("张三", 85, 92), ("李四", 78, 88), ("王五", 92, 95)]
- for name, math, english in students:
- avg = (math + english) / 2
- print(f"{name}: 数学{math}, 英语{english}, 平均{avg:.1f}")
- # 部分解包 + * 收集剩余元素
- data = [(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)]
- for first, second, *rest in data:
- print(f"前两个: ({first}, {second}), 剩余: {rest}")
复制代码
三、遍历字典
字典有三种遍历方式:遍历键(默认)、遍历 dict.values()、遍历 dict.items()(最常用)。items() 返回视图对象,不是拷贝,内存友好。遍历时可在 for 循环中直接解包 key 和 value。
- user = {"name": "张三", "age": 25, "city": "北京"}
- # 方式1:遍历键
- for key in user:
- print(key)
- # 方式2:遍历值
- for value in user.values():
- print(value)
- # 方式3:遍历键值对(推荐)
- for key, value in user.items():
- print(f"{key}: {value}")
- # 带索引遍历字典
- for i, (key, value) in enumerate(config.items(), 1):
- print(f"{i}. {key} = {value}")
- # 筛选遍历:根据值类型处理
- for key, value in config.items():
- if isinstance(value, bool):
- print(f"布尔配置: {key} = {value}")
- elif isinstance(value, int):
- print(f"数值配置: {key} = {value}")
复制代码
性能考量:keys()、values()、items() 返回视图对象,不占额外内存。如果需要多次遍历,可以转为 list 以加速。实测 10 万键值的字典,遍历键值对与仅遍历键的时间差距不大(约 0.1~0.2s),按需选择即可。
四、遍历集合
集合遍历无序,因哈希表实现。元素自动去重,成员检查 O(1)。若需有序输出,用 sorted()。结合集合运算(差集、交集、对称差)可以在循环前过滤数据。
- fruits_set = {"苹果", "香蕉", "橘子", "葡萄"}
- for fruit in fruits_set:
- print(fruit) # 每次输出顺序可能不同
- # 有序遍历
- for fruit in sorted(fruits_set):
- print(fruit)
- # 集合运算实例
- all_users = {"张三", "李四", "王五", "赵六", "钱七"}
- vip_users = {"张三", "赵六", "孙八"}
- banned_users = {"李四", "钱七"}
- active_vip = vip_users & all_users - banned_users
- for user in active_vip:
- print(f"给VIP用户 {user} 发福利!")
复制代码
五、嵌套遍历与并行遍历
二维列表(矩阵)可用双层 for 循环遍历,也可用列表推导式扁平化。需要并行遍历多个等长序列时用 zip(),它按最短序列截断;需要按最长序列则用 itertools.zip_longest。
- matrix = [[1,2,3],[4,5,6],[7,8,9]]
- # 双层遍历
- for row in matrix:
- for element in row:
- print(element, end=' ')
- print()
- # 带索引的遍历
- for i, row in enumerate(matrix):
- for j, element in enumerate(row):
- print(f"matrix[{i}][{j}] = {element}")
- # 并行遍历 zip
- names = ["张三","李四","王五"]
- ages = [25,30,28]
- for name, age in zip(names, ages):
- print(f"{name} {age}岁")
- # 按最长序列遍历(填充 None)
- from itertools import zip_longest
- short = [1,2]
- long_list = [1,2,3,4]
- for a,b in zip_longest(short, long_list, fillvalue=0):
- print(a, b)
复制代码
六、for-else 语句
else 块在循环正常结束(未遇到 break)时执行,非常适合做“查找后未找到”的默认处理。
- def find_user(users, target):
- for user in users:
- if user["name"] == target:
- print(f"找到用户: {user}")
- break
- else:
- print(f"没有找到名为 {target} 的用户")
- return None
- return user
复制代码
七、实战案例
以下两个类展示了 for 循环的综合运用。
- class ScoreAnalyzer:
- """学生成绩分析,嵌套遍历字典与列表"""
- def __init__(self, scores):
- self.scores = scores # {姓名: {科目: 分数}}
- def get_student_average(self):
- result = {}
- for name, subjects in self.scores.items():
- if subjects:
- avg = sum(subjects.values()) / len(subjects)
- result[name] = round(avg, 1)
- return result
- def get_subject_average(self):
- subject_scores = {}
- for name, subjects in self.scores.items():
- for subject, score in subjects.items():
- subject_scores.setdefault(subject, []).append(score)
- result = {}
- for subject, scores in subject_scores.items():
- result[subject] = round(sum(scores)/len(scores), 1)
- return result
- def get_top_students(self, n=3):
- totals = [(name, sum(subjects.values()))
- for name, subjects in self.scores.items()]
- totals.sort(key=lambda x: x[1], reverse=True)
- return totals[:n]
- def get_failing_students(self, pass_score=60):
- failing = set()
- for name, subjects in self.scores.items():
- for score in subjects.values():
- if score < pass_score:
- failing.add(name)
- break
- return failing
- # 测试数据
- scores = {
- "张三": {"语文":85, "数学":92, "英语":78, "物理":88},
- "李四": {"语文":90, "数学":58, "英语":95, "物理":72},
- "王五": {"语文":76, "数学":85, "英语":82, "物理":90},
- }
- analyzer = ScoreAnalyzer(scores)
- analyzer.generate_report() # 方法详见原文
复制代码- class ShoppingCart:
- """购物车,遍历 items 列表计算总价、打印小票"""
- def __init__(self):
- self.items = [] # (name, price, quantity)
- def add_item(self, name, price, quantity=1):
- for i, (item_name, _, item_qty) in enumerate(self.items):
- if item_name == name:
- self.items[i] = (item_name, price, item_qty + quantity)
- return
- self.items.append((name, price, quantity))
- def total_price(self):
- return sum(price * qty for _, price, qty in self.items)
- def print_receipt(self):
- print(f"{'商品':<20} {'单价':>8} {'数量':>6} {'小计':>8}")
- print("-"*45)
- for name, price, quantity in self.items:
- subtotal = price * quantity
- print(f"{name:<20} ¥{price:>7.2f} {quantity:>6} ¥{subtotal:>7.2f}")
- total = self.total_price()
- print(f"{'合计':<20} {'':>8} {sum(qty for _,_,qty in self.items):>6}件 ¥{total:>7.2f}")
复制代码
小结:for 循环遍历列表、元组、字典、集合,各有注意事项。遍历列表时建议用 enumerate 带索引,避免在循环中增删原列表;字典推荐用 items() 解包;集合遍历无序,可用 sorted() 固定顺序;for-else 提供优雅的未找到处理方案。掌握这些技巧能让代码更简洁、安全、高效。 |