【发布时间】:2010-11-20 02:31:35
【问题描述】:
我想过滤这个列表,
[0, 1, 1, 2, 2]
只离开
[0]
我正在努力以“pythonic”的方式做到这一点。没有嵌套循环可以吗?
【问题讨论】:
-
@Scott - 阅读标题。
-
@Markus - 是的,这有帮助!我想我一定要瞎了。在其他人认为我也是白痴之前,我将删除我原来的评论! :)
我想过滤这个列表,
[0, 1, 1, 2, 2]
只离开
[0]
我正在努力以“pythonic”的方式做到这一点。没有嵌套循环可以吗?
【问题讨论】:
您需要两个循环(或等效的一个循环和一个 listcomp,如下所示),但不需要嵌套循环:
import collections
d = collections.defaultdict(int)
for x in L: d[x] += 1
L[:] = [x for x in L if d[x] == 1]
此解决方案假定列表项是可散列的,也就是说,它们可用作字典、集合成员等的索引。
OP 表示他们关心对象 IDENTITY 而不是 VALUE(例如,两个子列表都值 [1,2,3,它们相等但可能不相同,不会被视为重复)。如果情况确实如此,那么这段代码是可用的,只需在两次出现时将d[x] 替换为d[id(x)],它将适用于列表 L 中的任何类型的对象。
可变对象(列表、字典、集合...)通常不可散列,因此不能以此类方式使用。默认情况下,用户定义的对象是可散列的(使用hash(x) == id(x)),除非它们的类定义了比较特殊方法(__eq__,__cmp__,...),在这种情况下,当且仅当它们的类还定义了一个__hash__ 方法。
如果列表 L 的项目不可散列,但 具有可比性(因此可排序),并且您不关心它们在列表中的顺序,您可以及时执行任务@ 987654329@,首先对列表进行排序,然后应用itertools.groupby(几乎但不完全按照另一个答案建议的方式)。
当您确实关心列表的原始顺序时,其他逐渐降低性能和增加通用性的方法可以处理不可散列的可排序对象(制作排序副本并在第二个循环中借助 bisect -- 也是 O(N log N) 但慢一点),并且对象的唯一适用属性是它们在相等性方面具有可比性(无法避免在最大一般情况下可怕的 O(N**2) 性能)。
如果 OP 可以澄清哪种情况适用于他的具体问题,我将很乐意提供帮助(特别是,如果他的对象是可散列的,那么我上面已经给出的代码就足够了;-)。
【讨论】:
[x for x in the_list if the_list.count(x)==1]
虽然这仍然是幕后的嵌套循环。
【讨论】:
the_list.count(x)==1 所在的那些,即仅出现一次的那些”
这是另一种面向字典的方式:
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
【讨论】:
.keys()。
d.keys() 每次调用它时都会生成一个新的列表对象。在 Python 3 中,它每次都会创建一个新的字典视图。使用k in d,总是。 dict.has_key 已被弃用,取而代之的是 key in dict。如果你循环使用l 而不是d,你就可以像 Alex 的版本一样维持秩序。这使得for i in l: d[i] = i in d 和[k for k in l if not d[k]] 在一起。
>>> l = [0,1,1,2,2]
>>> [x for x in l if l.count(x) is 1]
[0]
【讨论】:
is 比使用== 有什么优势吗?我知道 1 是一个足够小的数字,但is 在比较整数时实际上更快吗?
is 与数字一起使用,它仅适用于 cpython 优化了一些常用常量(如小 (语言的一部分。
is。这个解决方案是一种 O(N**2) 复杂度的方法,因为 list.count() 每次调用它时都会对列表进行全面扫描,并且你调用它 N 次。
本着与 Alex 的解决方案相同的精神,您可以使用 Counter/multiset(内置于 2.7,与 2.5 及更高版本兼容的配方)来做同样的事情:
In [1]: from counter import Counter
In [2]: L = [0, 1, 1, 2, 2]
In [3]: multiset = Counter(L)
In [4]: [x for x in L if multiset[x] == 1]
Out[4]: [0]
【讨论】:
l = [0,1,2,1,2]
def justonce( l ):
once = set()
more = set()
for x in l:
if x not in more:
if x in once:
more.add(x)
once.remove( x )
else:
once.add( x )
return once
print justonce( l )
【讨论】:
我认为实际的时间安排很有趣:
亚历克斯的回答:
python -m timeit -s "l = range(1,1000,2) + range(1,1000,3); import collections" "d = collections.defaultdict(int)" "for x in l: d[x] += 1" "l[:] = [x for x in l if d[x] == 1]"
1000 loops, best of 3: 370 usec per loop
我的:
python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "once = set()" "more = set()" "for x in l:" " if x not in more:" " if x in once:" " more.add(x)" " once.remove( x )" " else:" " once.add( x )"
1000 loops, best of 3: 275 usec per loop
sepp2k 的 O(n**2) 版本,以说明为什么复杂性很重要 ;-)
python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "[x for x in l if l.count(x)==1]"
100 loops, best of 3: 16 msec per loop
罗伯托的 + 排序:
python -m timeit -s "l = range(1,1000,2) + range(1,1000,3); import itertools" "[elem[0] for elem in itertools.groupby(sorted(l)) if elem[1].next()== 0]"
1000 loops, best of 3: 316 usec per loop
麦克的:
python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "d = {}" "for i in l: d[i] = d.has_key(i)" "[k for k in d.keys() if not d[k]]"
1000 loops, best of 3: 251 usec per loop
我喜欢最后一个,聪明又快 ;-)
【讨论】:
>>> l = [0,1,1,2,2]
>>> [x for x in l if l.count(x) == 1]
[0]
【讨论】:
在编码问题上出现了类似的问题。我的解决方案不是最优雅的,但它是我第一个自我召唤的解决方案,所以想分享!
testlist = [2,4,6,8,10,2,6,10]
def unique_elements(testlist):
final_list = []
for x in testlist:
if testlist.count(x)==1:
final_list.append(x)
else:
pass
print(final_list)
unique_elements(testlist)
【讨论】: