从列表中删除项目 - 在迭代期间 - 这个成语有什么问题?

mon*_*nny 24 python loops list

作为一个实验,我这样做了:

letters=['a','b','c','d','e','f','g','h','i','j','k','l']
for i in letters:
    letters.remove(i)
print letters
Run Code Online (Sandbox Code Playgroud)

最后一次打印显示并非所有项目都被删除了?(每隔一个人).

IDLE 2.6.2      
>>> ================================ RESTART ================================
>>> 
['b', 'd', 'f', 'h', 'j', 'l']
>>> 
Run Code Online (Sandbox Code Playgroud)

对此有何解释?如何重新编写以删除每个项目?

bad*_*adp 38

一些答案解释了为什么会发生这种情况,并解释一些你应该做的事情.我会无耻地把碎片放在一起.


这是什么原因?

因为Python语言旨在以不同方式处理此用例.文档清楚地表明:

修改循环中迭代的序列是不安全的(这只能发生在可变序列类型中,例如列表).如果您需要修改正在迭代的列表(例如,复制所选项目),则必须迭代副本.

强调我的.有关详细信息,请参阅链接页面 - 文档受版权保护,并保留所有权利.

你可以很容易地理解为什么你得到了你得到的东西,但它基本上是未定义的行为,可以很容易地改变而不会从构建到构建发出警告.只是不要这样做.

这就像想知道为什么i += i++ + ++i你的架构在你的语言编译器的特定构建上对你的架构做了什么呢 - 包括但不限于摧毁你的计算机让恶魔飞出你的鼻子 :)


如何重新编写以删除每个项目?

  • del letters[:] (如果需要更改对该对象的所有引用)
  • letters[:] = [] (如果需要更改对该对象的所有引用)
  • letters = [] (如果你只想使用一个新对象)

也许你只是想根据条件删除一些项目?在这种情况下,您应该迭代列表的副本.制作副本的最简单方法是使用[:]语法创建包含整个列表的切片,如下所示:

#remove unsafe commands
commands = ["ls", "cd", "rm -rf /"]
for cmd in commands[:]:
  if "rm " in cmd:
    commands.remove(cmd)
Run Code Online (Sandbox Code Playgroud)

如果您的支票不是特别复杂,您可以(也可能应该)过滤:

commands = [cmd for cmd in commands if not is_malicious(cmd)]
Run Code Online (Sandbox Code Playgroud)


Mic*_*yan 9

你不能迭代列表并同时改变它,而是遍历切片:

letters=['a','b','c','d','e','f','g','h','i','j','k','l']
for i in letters[:]: # note the [:] creates a slice
     letters.remove(i)
print letters
Run Code Online (Sandbox Code Playgroud)

也就是说,对于像这样的简单操作,你应该简单地使用:

letters = []
Run Code Online (Sandbox Code Playgroud)


小智 7

您无法修改正在迭代的列表,否则您将获得这种奇怪的结果类型.为此,您必须遍历列表的副本:

for i in letters[:]:
  letters.remove(i)
Run Code Online (Sandbox Code Playgroud)


Sil*_*ost 5

你想要做的是:

letters[:] = []
Run Code Online (Sandbox Code Playgroud)

要么

del letters[:]
Run Code Online (Sandbox Code Playgroud)

这将保留原始对象letters指向.其他选项,如,letters = []会创建一个新对象并指向letters它:旧对象通常会在一段时间后被垃圾收集.

并非所有值都被删除的原因是您在迭代时更改列表.

ETA:如果要从列表中过滤值,可以使用如下列表推导:

>>> letters=['a','b','c','d','e','f','g','h','i','j','k','l']
>>> [l for l in letters if ord(l) % 2]
['a', 'c', 'e', 'g', 'i', 'k']
Run Code Online (Sandbox Code Playgroud)


mam*_*moo 5

它会删除第一次出现,然后检查序列中的下一个数字.由于序列已经改变,它需要下一个奇数,依此类推......

  • 拿"a"
  • 删除"a" - >第一项现在是"b"
  • 拿下一个项目,"c"-...