0

我正在尝试为某人调试一些代码,并且遇到了一个相当奇怪的情况。该代码的目的是在给定列表中搜索重复项,然后返回一个没有重复项的列表。(请注意,编写代码的人选择简单地从列表中删除重复项,而我个人只是将每个值添加到一个新列表中。但是,我仍然对这种奇怪现象很感兴趣)。代码如下:

def remove_duplicates(duplicates):
    duplicates_del = duplicates 
    for i in duplicates_del:
        if duplicates_del.count(i) > 1:
            duplicates_del.remove(i)
    return duplicates_del

remove_duplicates([3, 3, 3, 3, 3, 3])

运行时,代码会返回[3, 3, 3],经过一些调试,我发现代码可以正常工作,直到duplicates_del.count(i)等于4。在下一轮,它将完全跳过for语句中的所有内容,直接进入return语句,导致我们得到的答案。

我了解到,将 if 语句更改为while duplicates_del.count(i) > 1:将使代码完美运行。

我查看了调试器的代码,并了解到有一个可以忽略计数的断点类。if 语句是否以某种方式触发了此断点,或者是否还有其他原因导致代码无法使用 if 语句而不是 while 循环完全运行?

4

2 回答 2

2

发生这种情况的原因是您在删除项目时正在迭代列表。这通常会导致意想不到的结果。看一眼:

L = [1, 2, 3, 4, 5]
for item in L:
    if item == 1 or item == 2 or item == 3:
        L.remove(item)
print L

输出是:

[2, 4, 5]

请注意,2它从未被删除。如果我们item在每个循环中打印,我们会得到:

1
3
5

python removes 后1,列表的顺序会发生变化,2不一定是循环中的下一项(实际上3是)。注意如何4也被跳过。


为避免此类行为,您必须遍历列表的副本。可悲的是,您所做的不是复制。这样做duplicates_del = duplicates将使两个对象引用相同的标识,因此更改一个元素将更改另一个元素。

你应该做这个:

def remove_duplicates(duplicates):
    for i in duplicates[:]: # Creates a copy of the list
        if duplicates.count(i) > 1:
            duplicates.remove(i)
    return duplicates
于 2013-11-07T03:12:21.453 回答
1

当您遍历它时,您正在从列表中删除。

通常,这意味着将跳过已删除的项目之后的项目。

在这种情况下remove,每次都删除第一个匹配元素,因此整个列表都被向下移动。列表迭代器没有看到列表已更改,因此递增到下一项。

于 2013-11-07T03:12:15.460 回答