python 中的 in 运算符在遍历的列表发生变化时的行为如何



我在尝试删除时遇到了一个有趣且令人费解的情况列表中的所有空字符串。我第一次写了下面的代码。

lst=['###','','@@@','','$$$','','','%%%','','&&&']
print "len:",len(lst)
iteration=1
for item in lst:
    print iteration,":",lst,":",len(lst),":","'%s'"%item
    if item!='':
        pass
    else:
        lst.remove(item)
    iteration+=1

它产生以下输出:

len: 10
1 : ['###', '', '@@@', '', '$$$', '', '', '%%%', '', '&&&'] : 10 : '###'
2 : ['###', '', '@@@', '', '$$$', '', '', '%%%', '', '&&&'] : 10 : ''
3 : ['###', '@@@', '', '$$$', '', '', '%%%', '', '&&&'] : 9 : ''
4 : ['###', '@@@', '$$$', '', '', '%%%', '', '&&&'] : 8 : ''
5 : ['###', '@@@', '$$$', '', '%%%', '', '&&&'] : 7 : '%%%'
6 : ['###', '@@@', '$$$', '', '%%%', '', '&&&'] : 7 : ''

注意:代码无法正常工作。中有一些空字符串输出。后来我发现了更好的方法,例如:列表推导:[x for x in lst if x!='']或创建一个新列表并将非空字符串复制到其中,这会发生比上面的代码更有效,因为它不涉及移动每次从列表中删除元素时的位置。

但是,我对上述代码的输出有一些疑问。

第一个问题是,为什么循环不运行十次(迭代次数在 最左边(,因为列表的原始长度是十。 其次,如果您查看最右边的列,您会发现它无法打印打印@@@字符串。它完全跳过了它!我的理论是in运算符是索引的糖(最有可能(,因此即使列表的长度发生变化该指数持续增加一。这可以解释为什么在第三次迭代中i 的值是空字符串而不是@@@,因为lst[2]''

使用 in 运算符时我需要知道什么吗?

任何时候你在循环中删除东西,你都会得到这样的奇怪结果。 如果遍历切片[:],字符串将不再消失

for item in lst[:]:

创建要迭代的副本,以便可以在不影响迭代的情况下操作列表的元素

这篇文章描述了当你在迭代列表时修改列表时会发生什么。

在内部,循环访问列表使用索引。此索引在循环中每次递增 1,并用于检索所需的元素。如果在迭代时删除一个元素,一个新元素将被"移动到"您正在查看的插槽中,然后循环的下一次迭代将查看下一个迭代,因此首先移动的元素永远不会被查看。

对此的一些解决方案包括:

  • 创建一个新列表(也许使用列表推导式(,而不是从旧列表中删除项目。如果需要,可以使用切片分配将此新列表分配回原始容器: lst[:] = (item for item if item != "") 这是最快的,因为它避免了在删除时多次移动项目。
  • 使用 reversed() 以相反的顺序迭代列表,这样您只会"移动"您已经看到的项目。
  • 循环访问列表的副本,但修改原始列表。

在您的情况下,您实际上不需要迭代列表。您只想从中删除空字符串。因此,第四种选择是删除空字符串,直到没有空字符串为止!

try:
     while True:
         lst.remove('')   # deletes first empty string
except ValueError:        # no more empty strings
     pass

最新更新