过滤列表,仅保留出现一次的对象
我想对这个列表进行筛选,
[0, 1, 1, 2, 2]
只留下
[0]
我在尝试用一种“Python风格”的方法来实现这个,但我不知道是否可以不使用嵌套循环。
9 个回答
[x for x in the_list if the_list.count(x)==1]
虽然这背后仍然是一个嵌套循环。
你需要用两个循环(或者说一个循环和一个列表推导式,像下面这样),但不是嵌套的循环:
import collections
d = collections.defaultdict(int)
for x in L: d[x] += 1
L[:] = [x for x in L if d[x] == 1]
这个解决方案假设列表中的项目是可哈希的,也就是说,它们可以用作字典的索引、集合的成员等等。
提问者表示他们关心的是对象的身份,而不是值(比如说,两个子列表都为[1,2,3
,虽然值相等但可能不是同一个对象,这种情况不会被认为是重复)。如果确实是这样,那么这段代码可以使用,只需将d[x]
替换为d[id(x)]
,在两个地方都这样做,它就能处理列表L中的任何类型的对象。
可变对象(比如列表、字典、集合等)通常是不可哈希的,因此不能以这种方式使用。用户自定义的对象默认是可哈希的(hash(x) == id(x)
),除非它们的类定义了比较的特殊方法(__eq__
, __cmp__
等),在这种情况下,只有当它们的类也定义了__hash__
方法时,它们才是可哈希的。
如果列表L中的项目不可哈希,但可以进行不等比较(因此可以排序),而且你不在乎它们在列表中的顺序,你可以通过先对列表进行排序,然后使用itertools.groupby
来完成这个任务,时间复杂度为O(N log N)
(几乎是另一种答案建议的方式,但不完全相同)。
其他方法的性能逐渐下降,但通用性逐渐增加,可以处理不可哈希的可排序对象,当你在乎列表的原始顺序时(制作一个排序的副本,然后在第二个循环中用bisect
检查重复项——同样是O(N log N)
,但稍微慢一点),以及只有可比较相等属性的对象(在这种最通用的情况下,无法避免令人头疼的O(N**2)
性能)。
如果提问者能澄清他具体问题适用哪种情况,我会很乐意提供帮助(特别是如果他的问题中的对象是可哈希的,那么我上面给出的代码应该就足够了;-)。
这里有另一种以字典为中心的方法:
l = [0, 1, 1, 2, 2]
d = {}
for i in l: d[i] = i in d
[k for k in d if not d[k]] # unordered, loop over the dictionary
[k for k in l if not d[k]] # ordered, loop over the original list