Python中for循环语句实战案例

发布时间:2026/10/15 6:56:29
Python中for循环语句实战案例
前言很多人从 C 或 Java 转过来会先入为主地把for理解成「计数器循环」——初始化、判断、自增三段式。Python 的for完全不是这个模型。它的语义是遍历一个可迭代对象iterable把里面的元素一个一个取出来。range(10)看起来很像个计数器其实它只是可迭代对象里最常见的一种。这个理解差异会带来一批连锁误解以为for循环变量有独立的块级作用域其实泄漏到外层、以为for一定要写range(len(x))其实直接遍历元素更清晰、以为range(1, 5)会包含 5它是左闭右开。本文用一组实战案例把for的常见用法铺开带序号遍历、同时遍历多个序列、遍历字典、for ... else做查找、嵌套循环里的break、以及反转与排序遍历。每个例子都能直接复制运行。顺便说一句 Python 2xrange这个名字在 Python 3 里已经被移除因为range本身就是惰性的——它返回的是一个按需产生整数的对象不会像 Python 2 的range那样一次性生成整个列表。同理dict.iteritems()/iterkeys()/itervalues()也都没了一律用.items()/.keys()/.values()它们返回的是视图view而不再是列表。一、for到底在遍历什么for x in obj的执行过程只有两步先调用iter(obj)拿到一个迭代器iterator然后反复对这个迭代器调用next()直到抛出StopIteration。所以任何实现了__iter__的对象都能被for遍历——列表、元组、字符串、字典、集合、文件对象、生成器、range对象全都是。理解这一点就能理解下面两件事生成器只能遍历一次。第一次for把它消耗完了第二次for什么都拿不到因为它本身既是可迭代对象又是迭代器。遍历字典拿到的是键key不是键值对。要键值对得显式写.items()。你想要的写法返回什么Python 3遍历元素for x in seq元素本身带序号for i, x in enumerate(seq)序号 元素并行遍历多个for a, b in zip(s1, s2)各序列对应位置遍历字典键for k in d键遍历键值对for k, v in d.items()键、值倒序遍历for x in reversed(seq)反序元素排序后遍历for x in sorted(seq, key...)排序后的元素数字区间for i in range(a, b)整数左闭右开二、实战案例案例一带序号遍历# 适用于 Python 3.8names [alice, bob, carol]for index, name in enumerate(names, start1):print(index, name)# 1 alice / 2 bob / 3 carolenumerate(iterable, start0)的第二个参数是起点默认从 0 开始。不要写for i in range(len(names)): name names[i]那种写法既啰嗦又容易写错下标。案例二同时遍历两个序列# 适用于 Python 3.8skus [A001, A002, A003]prices [399, 129, 59]for sku, price in zip(skus, prices):print(sku, , price)zip会在最短的序列耗尽时停止。也就是说skus有三个元素而prices只有两个时第三个 sku 会被静默丢掉。如果这属于数据错误、你希望它报错Python 3.10 起可以写zip(skus, prices, strictTrue)长度不一致会抛ValueError。案例三遍历字典# 适用于 Python 3.8# 3.7 起字典保证按插入顺序遍历scores {math: 90, english: 85, history: 78}for key in scores: # 等价于 scores.keys()print(科目:, key)for value in scores.values():print(分数:, value)for subject, score in scores.items():print(subject, -, score).keys()、.values()、.items()返回的都是视图它们不是列表会随字典内容动态变化。想固定下来要显式list(scores.items())。案例四for ... else做查找# 适用于 Python 3.8names [alice, bob, carol]target davefor name in names:if name target:print(找到了:, name)breakelse:print(列表中没有, target)else子句只在循环没有被break中断时执行。它天生适合表达找遍了都没有这种语义比设一个found标志位再判断要干净。注意如果循环体里有returnelse也可能被执行——它是没有 break不是没有提前退出。案例五嵌套循环里的break# 适用于 Python 3.8matrix [[1, 2, 3], [4, 5, 6], [7, 8, 9]]position Nonefor row_index, row in enumerate(matrix):for col_index, value in enumerate(row):if value 5:position (row_index, col_index)break # 只跳出内层循环if position is not None:break # 外层需要再判断一次print(5 的位置:, position) # (1, 1)break只作用于它所在的那一层循环。要一次跳出多层标准写法是把逻辑抽成函数用return直接返回或者用for ... else配合标志位。Python 没有break 2这种带层数的语法。案例六反转、排序与区间# 适用于 Python 3.8for ch in reversed(abc):print(ch) # c b afor value in sorted([3, 1, 2], reverseTrue):print(value) # 3 2 1for i in range(10, 0, -3):print(i) # 10 7 4 1reversed()要求对象要么是序列要么实现了__reversed__。它不能反转任意迭代器比如把一个生成器传进去会直接TypeError。sorted()返回新列表原序列不动如果原对象是列表且你想原地排序用lst.sort()——注意它返回None别写for x in lst.sort()。常见坑点1. 遍历列表的同时增删元素❌for item in nums:if item % 2 0:nums.remove(item) # 索引在变会漏掉元素✅nums [x for x in nums if x % 2 ! 0]一边走一边动迭代器内部的索引和实际列表错位结果是删了一些但没删干净且不报错。2. 遍历字典时改它的大小❌for k in d: d[new_ k] 1→RuntimeError: dictionary changed size during iteration✅for k in list(d): d[new_ k] 1先转成列表把键拷出来再改原字典。改值不改键集合是允许的。3. 以为range(1, 5)包含 5❌list(range(1, 5))期待得到[1, 2, 3, 4, 5]✅ 它是左闭右开实际是[1, 2, 3, 4]要含 5 得写range(1, 6)4. 用range(len(x))取元素❌for i in range(len(names)):print(names[i])✅for name in names: print(name)只有确实需要下标时才用enumerate。range(len(...))既慢又容易越界。5. 以为for循环变量只在循环内有效❌ 在外层用同名变量以为不会被覆盖 ✅ 要知道for不会创建新作用域循环结束后变量保留最后一次的值for i in range(3):passprint(i) # 2i 仍然存在6. 在闭包里引用循环变量❌funcs [lambda: i for i in range(3)]print([f() for f in funcs]) # [2, 2, 2]全是最后一个值✅funcs [lambda ii: i for i in range(3)]print([f() for f in funcs]) # [0, 1, 2]这是延迟绑定闭包捕获的是变量本身不是当时的值。7. 用reversed()反转生成器❌reversed(x for x in range(3))→TypeError: generator object is not reversible✅ 先物化reversed(list(x for x in range(3)))或用sorted(..., reverseTrue)8. 忽略zip的截断行为❌ 拿两个长度不同的列表zip后直接当成一一对应 ✅ Python 3.10 用zip(a, b, strictTrue)主动暴露长度不一致 静默截断会让少的那部分数据消失得无声无息。总结需求推荐写法要避开的写法带序号enumerate(seq, start1)range(len(seq))并行遍历zip(a, b)用下标同时访问两个列表键值对d.items()d.iteritems()Python 2已移除找不着就报错for...else手动设标志位跳多层循环抽成函数用return层层设标志位过滤重建列表推导式边遍历边removefor在 Python 里是遍历而不是计数接受这个心智模型之后绝大多数写法问题都会自然消失不再需要下标、不再需要手写标志位、不再需要担心越界。真正需要盯住的只剩下不要在遍历过程中改变被遍历的对象这一条。