[英]how to safely remove elements from a list in Python
我遍历一个列表并删除满足我条件的元素。 但是,为什么这不起作用,如下所述? 谢谢。
>>> a=[ i for i in range(4)]
>>> a
[0, 1, 2, 3]
>>> for e in a:
... if (e > 1) and (e < 4):
... a.remove(e)
...
>>> a
[0, 1, 3]
>>> a=[ i for i in range(4)]
>>> for e in a:
... if (e > -1) and (e < 3):
... a.remove(e)
...
>>> a
[1, 3]
在迭代时你无法改变某些东西。 结果很奇怪,反直觉,几乎不是你想要的。 实际上,许多集合明确地禁止这样做(例如set和dicts)。
相反,迭代一个副本( for e in a[:]: ...
)或者,而不是修改现有列表,过滤它以获得包含所需项目的新列表( [e for e in a if ...]
)。 请注意,在许多情况下,您不必再次迭代进行过滤,只需将过滤与数据生成合并即可。
你为什么不在列表理解中最初这样做? 例如
[i for i in range(4) if i <= 1 or i >= 4]
您也可以使用它从现有列表构建新列表,例如
[x for x in a if x <= 1 or x >= 4]
过滤的想法很好,但它忽略了一些列表可能非常大并且要删除的元素数量可能非常小的点。
在这种情况下,答案是记住要删除的元素的列表索引,然后遍历索引列表,从最大到最小排序,删除元素。
可视化它的最简单方法是考虑迭代处理列表偏移而不是实际项目 - 对第一个项目执行操作,然后对第二个项目执行操作,然后执行第三个项目,直到项目用完为止。 如果更改列表中的项目数,则会更改列表中所有剩余项目的偏移量:
lst = [1,2,3,4]
for item in lst:
if item==2:
lst.remove(item)
else:
print item
print lst
结果是
1
4
[1,3,4]
如果你像这样走过它是有道理的:
[1,2,3,4]
^
first item is not 2, so print it -> 1
[1,2,3,4]
^
second item is 2, so remove it
[1,3,4]
^
third item is 4, so print it -> 4
唯一真正的解决方案是在迭代时不要更改列表中的项目数 。 将要保留的项目复制到新列表,或跟踪要删除的值,并在单独的传递中执行按值删除。
在迭代它时,从列表中删除元素是不安全的。 因为存在过滤功能。 它需要一个函数(允许一个参数)和一个iterable(在这种情况下是你的列表)。 它返回一个相同类型的新迭代(这里再次列出),其中应用于该元素的函数的元素返回True:
在你的情况下,你可以使用这样的lambda函数:
a = filter(lambda x: x > 1 and x < 4, range(4))
或者如果你已经有了这个清单:
a = range(4)
a = filter(lambda x: x > 1 and x < 4, a)
请记住,如果您使用的是python3,它将返回迭代器而不是列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.