Я недавно ответил на связанный вопрос, чтобы установить все дубликаты в списке, используя генератор. Преимущество этого метода в том, что если его использовать просто для определения «есть ли дубликат», то вам просто нужно получить первый элемент, а остальные можно игнорировать, что является окончательным ярлыком.
Это интересный подход, основанный на множествах, который я адаптировал прямо из moooeeeep :
def getDupes(l):
seen = set()
seen_add = seen.add
for x in l:
if x in seen or seen_add(x):
yield x
Соответственно, полный список обманщиков будет list(getDupes(etc))
. Чтобы просто проверить «если» есть дублирование, его следует обернуть следующим образом:
def hasDupes(l):
try:
if getDupes(l).next(): return True # Found a dupe
except StopIteration:
pass
return False
Это хорошо масштабируется и обеспечивает стабильное время работы, где бы ни был дубликат в списке - я протестировал со списками до 1 м записей. Если вы знаете что-то о данных, в частности, о том, что дупсы, скорее всего, появятся в первой половине, или о других вещах, которые позволяют исказить ваши требования, например о необходимости получения реальных дупликов, то есть пара действительно альтернативных локаторов дупе это может превзойти. Я рекомендую два ...
Простой подход, основанный на диктовке, очень удобочитаемый:
def getDupes(c):
d = {}
for i in c:
if i in d:
if d[i]:
yield i
d[i] = False
else:
d[i] = True
Использование itertools (по сути, ifilter / izip / tee) в отсортированном списке, очень эффективно, если вы получаете все дубли, хотя и не так быстро, чтобы получить только первое:
def getDupes(c):
a, b = itertools.tee(sorted(c))
next(b, None)
r = None
for k, g in itertools.ifilter(lambda x: x[0]==x[1], itertools.izip(a, b)):
if k != r:
yield k
r = k
Это были лучшие исполнители из подходов, которые я пробовал для полного списка , причем первый дублирование встречалось в любом месте списка элементов 1 м от начала до середины. Было удивительно, насколько мало добавилось в шаге сортировки. Ваш пробег может отличаться, но вот мои конкретные результаты:
Finding FIRST duplicate, single dupe places "n" elements in to 1m element array
Test set len change : 50 - . . . . . -- 0.002
Test in dict : 50 - . . . . . -- 0.002
Test in set : 50 - . . . . . -- 0.002
Test sort/adjacent : 50 - . . . . . -- 0.023
Test sort/groupby : 50 - . . . . . -- 0.026
Test sort/zip : 50 - . . . . . -- 1.102
Test sort/izip : 50 - . . . . . -- 0.035
Test sort/tee/izip : 50 - . . . . . -- 0.024
Test moooeeeep : 50 - . . . . . -- 0.001 *
Test iter*/sorted : 50 - . . . . . -- 0.027
Test set len change : 5000 - . . . . . -- 0.017
Test in dict : 5000 - . . . . . -- 0.003 *
Test in set : 5000 - . . . . . -- 0.004
Test sort/adjacent : 5000 - . . . . . -- 0.031
Test sort/groupby : 5000 - . . . . . -- 0.035
Test sort/zip : 5000 - . . . . . -- 1.080
Test sort/izip : 5000 - . . . . . -- 0.043
Test sort/tee/izip : 5000 - . . . . . -- 0.031
Test moooeeeep : 5000 - . . . . . -- 0.003 *
Test iter*/sorted : 5000 - . . . . . -- 0.031
Test set len change : 50000 - . . . . . -- 0.035
Test in dict : 50000 - . . . . . -- 0.023
Test in set : 50000 - . . . . . -- 0.023
Test sort/adjacent : 50000 - . . . . . -- 0.036
Test sort/groupby : 50000 - . . . . . -- 0.134
Test sort/zip : 50000 - . . . . . -- 1.121
Test sort/izip : 50000 - . . . . . -- 0.054
Test sort/tee/izip : 50000 - . . . . . -- 0.045
Test moooeeeep : 50000 - . . . . . -- 0.019 *
Test iter*/sorted : 50000 - . . . . . -- 0.055
Test set len change : 500000 - . . . . . -- 0.249
Test in dict : 500000 - . . . . . -- 0.145
Test in set : 500000 - . . . . . -- 0.165
Test sort/adjacent : 500000 - . . . . . -- 0.139
Test sort/groupby : 500000 - . . . . . -- 1.138
Test sort/zip : 500000 - . . . . . -- 1.159
Test sort/izip : 500000 - . . . . . -- 0.126
Test sort/tee/izip : 500000 - . . . . . -- 0.120 *
Test moooeeeep : 500000 - . . . . . -- 0.131
Test iter*/sorted : 500000 - . . . . . -- 0.157