不要使用.count()
,因为它会扫描每个元素的列表。此外,如果它们在输入中出现 3 次或更多次,它会将项目多次添加到输出中。
你最好在这里使用一个生成器函数,它只产生它以前见过的项目,但只产生一次:
def unique_plurals(lst):
seen, seen_twice = set(), set()
seen_add, seen_twice_add = seen.add, seen_twice.add
for item in lst:
if item in seen and item not in seen_twice:
seen_twice_add(item)
yield item
continue
seen_add(item)
[list(unique_plurals(c)) for c in col]
这仅在每个列表中迭代一次(与使用 a 不同Counter()
)。
这种方法要快得多:
>>> timeit('[[k for k, v in OrderedCounter(el).iteritems() if v != 1] for el in col]', 'from __main__ import col, OrderedCounter')
52.00807499885559
>>> timeit('[[k for k, v in Counter(el).iteritems() if v != 1] for el in col]', 'from __main__ import col, Counter')
15.766052007675171
>>> timeit('[list(unique_plurals(c)) for c in col]', 'from __main__ import col, unique_plurals')
6.946599006652832
>>> timeit('[list(unique_plurals_dict(c)) for c in col]', 'from __main__ import col, unique_plurals_dict')
6.557853937149048
这比方法快了大约 8 倍,是OrderedCounter
方法的 2.2 倍Counter
。
不过,Jon 的单字典加计数器方法更快!
但是,如果您只需要消除仅出现一次的值,但保持其余部分(包括重复)完整,那么您可以使用(借用 Jon):
from itertools import count
from collections import defaultdict
def nonunique_plurals(lst):
seen = defaultdict(count)
for item in lst:
cnt = next(seen[item])
if cnt:
if cnt == 1:
# yield twice to make up for skipped first item
yield item
yield item
这会产生:
>>> [list(nonunique_plurals(c)) for c in col]
[['red', 'red', 'yellow', 'yellow'], ['pink', 'pink', 'brown', 'brown']]
>>> timeit('[non_uniques(c) for c in col]', 'from __main__ import col, non_uniques')
17.75499200820923
>>> timeit('[list(nonunique_plurals(c)) for c in col]', 'from __main__ import col, unique_plurals')
9.306739091873169
这几乎是 FMc 提出Counter()
的解决方案速度的两倍,但它并没有准确地保留顺序:
>>> list(nonunique_plurals(['a', 'a', 'b', 'a', 'b', 'c']))
['a', 'a', 'a', 'b', 'b']
>>> non_uniques(['a', 'a', 'b', 'a', 'b', 'c'])
['a', 'a', 'b', 'a', 'b']