Python for 循环中的常见陷阱与解决方案

Python 中的 for 循环和其他语言中的 for 循环工作方式是不一样的,今天就带你深入了解 Python 的 for 循环,看看它是如何工作的,以及它为什么按照这种方式工作。

https://mp.weixin.qq.com/s?__biz=MjM5NjMyMjUzNg%3D%3D&mid=2448130798&idx=1&sn=baa1799ceeee81dc05ba530845c03b9d&chksm=b2f42f698583a67f7c1066dc2778809327b0ffc0e0d314b2d8757fccf794137477eadcca2285&scene=0


Python for 循环中的常见陷阱与解决方案

38 回复

感觉写的有点误导人,squares 那个不能叫陷阱,只不过因为你预设了其它编程语言的概念而导致的错误,我觉得要讲 python 就直接讲 python,没有必要拿其它语言里的概念来对比

我发现许多讲编程语言的都喜欢这种方式,比如学过 java 的讲 python 或者 javascript,总是非常激动讲函数作为一等对象的特性,本来很自然的概念,让这些人激动的一讲,反而容易误导别人


在Python里写for循环,有几个坑新手很容易掉进去,我结合代码给你讲讲。

1. 在循环中修改正在迭代的列表 这是最经典的错误。你想删掉列表里所有偶数,可能会这么写:

numbers = [1, 2, 3, 4, 5, 6]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)
print(numbers)  # 输出:[1, 3, 5, 6]  ← 6没删掉!

问题在于循环时列表索引会错位。正确做法是创建新列表或反向迭代

# 方法1:列表推导式(推荐)
numbers = [1, 2, 3, 4, 5, 6]
numbers = [num for num in numbers if num % 2 != 0]

# 方法2:迭代副本
numbers = [1, 2, 3, 4, 5, 6]
for num in numbers[:]:  # 注意这里的切片副本
    if num % 2 == 0:
        numbers.remove(num)

# 方法3:反向迭代(删除元素时安全)
numbers = [1, 2, 3, 4, 5, 6]
for i in range(len(numbers)-1, -1, -1):
    if numbers[i] % 2 == 0:
        del numbers[i]

2. 循环变量泄露 循环结束后,循环变量依然存在:

for i in range(5):
    pass
print(i)  # 输出:4  ← 你可能以为会报错

这在某些情况下会导致bug。如果担心,可以在循环外用del i显式删除。

3. 嵌套循环中的变量混淆

matrix = [[1, 2], [3, 4]]
for row in matrix:
    for row in matrix:  # 重用变量名!
        print(row)

内层循环会覆盖外层变量。给不同层级的循环变量起不同的名字就能避免。

4. 迭代器耗尽 生成器和map()zip()返回的是迭代器,只能消费一次:

data = zip([1, 2], ['a', 'b'])
list(data)  # 第一次消费:[(1, 'a'), (2, 'b')]
list(data)  # 第二次:[]  ← 空了!

如果需要重复使用,先转换成列表:data = list(zip(...))

5. 用for-else的误解 else在循环正常完成(没被break打断)时执行:

for item in [1, 3, 5]:
    if item % 2 == 0:
        print("找到偶数")
        break
else:
    print("没找到偶数")  # 会执行这个

很多人以为else是循环失败才执行,其实正好相反。

总结: 修改列表时创建副本,注意变量作用域,记住迭代器只能消费一次。

所以说要理解语法先。这三个应该不至于叫陷阱吧,毕竟语法就是这样的。generator 你迭代完就是玩了;迭代 dict 的时候就是迭代的 key。
对其他语言来的一开始可能有点疑惑,但是 Python 语法确实就是这么写的~如果对 Python 熟悉就不会觉得这个是「陷阱」吧。

这种文章确实挺误导人的,可迭代对象和 for 循环本身就是两个东西,不能混为一谈。

至少第一个不能叫陷阱吧。
生成器本身就是为了与普通的列表区分来的。生成器所使用的场景也是迭代前不生成这些数据,迭代后不在需要这些数据。如果需要多次使用的数据,为什么不直接用「列表」或者其他的数据结构而使用生成器呢?

要么就 for a in b,要么就 for i in range(0:len(b)),和 java c 的 for 就不是一回事

一点用词的区别,被作者拿来大肆渲染

一点雕虫小技被作者渲染得神乎其神

这种文章就是毒瘤,善于营销但是不善于合理引导

垃圾文章

已 block

能否控制该营销号发帖?

既然都说了「生成器」,那不知道生成器只可遍历一次?

逻辑是先有定义才有判断的,定义的意思没搞清楚、不准确,判断就会错误,就叫做「逻辑不通」了

有空看这些半吊子博文不如多熟悉官方 ref

辣鸡,block

本来还以为要从 cpython 源代码分析 for 的执行原理,点开一看都是些没啥营养的,初学者才会犯的错

标题党是万恶之源

分清列表和生成器…

这种文章俗称标题党

看的我竟然笑了~唉

Generator、Iterator、Iterable,啥子陷阱,搞什么大新闻!

既然 生成器中没有了东西,再迭代为何 python 不抛异常??

生成器内部就是这样实现的,看这个还不如去看官方教程,或者推荐看 简明 python 教程,也比看这个好。

这是来搞笑的?

LZ 是 UC 出来的?起个标题都这样吸引眼球?

我觉得人家虽然写得不好,可也算是用心去写了,V2 什么时候风气这样了?你们觉得简单不看就行了嘛,干嘛喷人家?

这种文章真恶心,讲生成器就好好讲,文章开头说什么陷阱,py 入门书都会说明生成器的概念和行为。

垃圾文章,看到就烦。Block

列表解析外部用小括号返回的是生成器,生成器是一次性的,跟 for 循环有啥关系,更谈不上陷阱

UC 员工吗?

python 一开始的语法里面就有啊,入门书里面还专门提了这个区别;已经 block

一句话总结 ()的出来的 express comprehension 是 generator 不是 tuple

标题党

公众号毒瘤

这个 不正经程序员 对 python 其实只是一知半解,知其然不知其所以然的程序,主要是做小白粉丝营销的。

https://www.v2ex.com/t/466147
https://www.v2ex.com/t/466161

震惊,某 Python 半吊子博主发垃圾文被大佬按在地上摩擦!!!

好奇公众号的链接为什么有的这么长 有的短 比如 https://mp.weixin.qq.com/s/oc0hvAr-FoJFtiDF5x2p4Q

楼主在经营自己的公众号无疑。
从纯技术的角度讨论说,python 这样做也是有问题的,这样的语法正常的期待也是生成一个 tuple 而不是 generator。

就像:
1.type([1,2,3]) = list
2.type([x for x in xx]) = list
3.type((1,2,3)) = tuple
4.type((x for x in xx)) == generator (黑人问号???)
我猜是需要优化某总语法的性能才这样做,但也是反人类的。

就像
type((1)) = int
type((1,)) = tuple

一样

回到顶部