在Python中遍历列表时删除元素

15

在Java中,我可以使用一个 Iterator ,然后使用迭代器的 .remove() 方法来删除迭代器返回的最后一个元素,就像这样:

import java.util.*;

public class ConcurrentMod {
    public static void main(String[] args) {
        List<String> colors = new ArrayList<String>(Arrays.asList("red", "green", "blue", "purple"));
        for (Iterator<String> it = colors.iterator(); it.hasNext(); ) {
            String color = it.next();
            System.out.println(color);
            if (color.equals("green"))
                it.remove();
        }
        System.out.println("At the end, colors = " + colors);
    }
}

/* Outputs:
red
green
blue
purple
At the end, colors = [red, blue, purple]
*/

我该如何用Python实现这个?我无法在for循环迭代列表时修改它,因为这会导致跳过一些元素(请参见此处)。而且似乎没有Java中 Iterator 接口的等价物。


我想知道是否使用反向迭代器可能是一个解决办法。对此有什么想法吗?这比复制列表要好。 - Craig McQueen
这是一个经常被问到的问题... - Jochen Ritzel
4个回答

30

在Python中最好的方法是创建一个新列表,最好使用列表推导式,并将其设置为旧列表的[:],例如:

最佳做法是使用列表分片符号[:]来获得完整的副本,而不是直接复制指针引用。

colors[:] = [c for c in colors if c != 'green']

不要像某些答案所建议的那样使用colors =,因为这只是重新绑定名称,并最终会留下一些引用到旧的 "body"。 colors[:] = 在所有方面都要好得多。;-)


2
列表推导式是最佳的选择。 - hughdbrown
colors = list(c for c in colors if c != 'green') - dugres
@dugres:不完全正确:colors = list(...)会重新绑定。Alex坚持认为最好不要让无用的列表在内存中悬空。 - Eric O. Lebigot
好的,如果有其他需要关注的引用(由于某种原因,这种情况很少发生在我身上),那么colors[:] = ...仅会更好。不过,列表理解绝对是可行的方法。 - Devin Jeanpierre
1
@Devin,不仅适用于其他参考。例如,如果colors是全局变量,在函数中执行colors=需要额外的global colors,而colors[:] =则不需要。在所有Python版本中,旧列表的GC并不会立即发生。等等:将值分配给name[:]永远不会有任何缺点,而将值分配给name通常会有许多缺点(包括偶尔出现的令人困惑的错误,其中“很少为您”情况确实发生,但您习惯了错误的方式),因此对于正确的方式name[:]=来说,这是一种无意义的隐藏,而对于错误的方式name=来说,则是一种反对。只有一种明显的方法... - Alex Martelli
3
虽然不是荷兰人的话可能并不明显。;-) - Alex Martelli

25

遍历列表副本

for c in colors[:]:
    if c == 'green':
        colors.remove(c)

为什么要使用colors[:]而不是colors? - hughdbrown
5
colors[:]是一个副本(一种奇怪但惋惜的拼写方式,相当于list(colors)),因此它不会被.remove方法所影响。 - Alex Martelli
1
唯一称其更符合惯用语的原因是因为stdlib复制模块文档引用了它。尽管如此,我仍然会使用list(otherlist)进行复制(或可能是copy.copy(otherthing))。 - Devin Jeanpierre

4
您可以使用过滤函数:
>>> colors=['red', 'green', 'blue', 'purple']
>>> filter(lambda color: color != 'green', colors)
['red', 'blue', 'purple']
>>>

0

或者你也可以这样做

>>> colors = ['red', 'green', 'blue', 'purple']
>>> if colors.__contains__('green'):
...     colors.remove('green')

3
使用.__contains__()没有比 'green' in colors 更好的优势。 - Roberto Bonvallet
1
另外,colors.remove() 只会移除第一个出现的元素,而不是所有出现的元素。 - Eric O. Lebigot
2
解决方案可以通过以下方式实现:while 'green' in colors: colors.remove('green')。当然,这是O(n**2),而更好的解决方案是O(n)。 - Devin Jeanpierre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接