补一个容易和帖子里的坑搅在一起的情况。你举的是 for i in range(len(lst)) 按索引走、再 lst.remove(lst[i]) 的写法,跳过的机理说得很准。但 Python 里还有另一种常见写法,就是 for x in lst: 然后 lst.remove(x),它走的不是索引移位那套,而是 list 内部 iterator 靠隐藏游标推进。这时候删当前元素,行为更不可预测,有时跳过、有时多删,取决于具体序列,而且 Python 一句不报。所以严格讲,那句"迭代进行中别动本体"其实罩着两类不同机理的陷阱。
顺着你"挑一个解法就够"的说法,我想补个性能视角。for x in lst[:]: lst.remove(x) 看着最省事,可 .remove() 每次得从头线性扫一遍找元素,整体是 O(n²);列表推导式 [x for x in lst if not 条件] 一遍扫完重建,是 O(n)。量小无所谓,几万条往上差距就出来了。我的取舍一般是能推导式就推导式,真要在循环里顺手干点别的才退而用副本遍历。
再往外扩一点,这坑在各语言里待遇差很多。Java 的 Iterator 专门设计了 .remove(),就是给你遍历中删当前项的;C++ 的 erase 返回下一个合法迭代器,语言把"怎么安全改"写进了契约。反倒是 Python 的 list 最宽容,不拦不报,才养出一堆静默 bug。同是 Python,dict 和 set 遍历中改大小会直接抛 RuntimeError,list 偏偏不拦。这正应了你那句"比直接崩了还恶心":有时候报错反而是福气。
你最后 debug 到凌晨、发现是手痒在循环里改了集合那段太真实了。顺嘴一问,你那会儿用的是 index 版还是 value 版?