Edit 2020
À partir de CPython/PyPy 3.6 (et en tant que garantie du langage dans la version 3.7), le langage simple dict
est ordonnée par insertion, et encore plus efficace que la méthode (également implémentée en C) collections.OrderedDict
. Ainsi, la solution la plus rapide, et de loin, est aussi la plus simple :
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(dict.fromkeys(items))
[1, 2, 0, 3]
Comme list(set(items))
cela repousse tout le travail vers la couche C (sur CPython), mais puisque dict
sont ordonnés par insertion, dict.fromkeys
ne perd pas l'ordre. C'est plus lent que list(set(items))
(cela prend 50 à 100 % plus de temps en général), mais beaucoup plus rapide que n'importe quelle autre solution de préservation de l'ordre (il faut environ la moitié du temps de Les piratages impliquant l'utilisation de set
s dans une listecomp ).
Edit 2016
En tant que Raymond a souligné dans python 3.5+ où OrderedDict
est implémenté en C, l'approche par la compréhension de liste sera plus lente que OrderedDict
(sauf si vous avez réellement besoin de la liste à la fin - et encore, seulement si l'entrée est très courte). La meilleure solution pour les versions 3.5+ est donc la suivante OrderedDict
.
Important Edit 2015
Comme @abarnert notes, le more_itertools
bibliothèque ( pip install more_itertools
) contient un unique_everseen
fonction qui est construite pour résoudre ce problème sans aucune illisible ( not seen.add
) mutations dans les compréhensions de listes. C'est également la solution la plus rapide :
>>> from more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]
Une simple importation de bibliothèque et pas de bidouillage. Ceci provient d'une implémentation de la recette itertools unique_everseen
ce qui ressemble à :
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
En Python 2.7+
le site idiome courant accepté (qui fonctionne mais n'est pas optimisé pour la vitesse, j'utiliserais maintenant unique_everseen
) pour cette utilisation collections.OrderedDict
:
Durée d'exécution : O(N)
>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]
Ça a l'air beaucoup plus joli que :
seen = set()
[x for x in seq if x not in seen and not seen.add(x)]
et n'utilise pas le Mauvais coup :
not seen.add(x)
qui s'appuie sur le fait que set.add
est une méthode in-place qui renvoie toujours None
donc not None
évalue à True
.
Notez cependant que la solution du hack est plus rapide en vitesse brute bien qu'elle ait la même complexité d'exécution O(N).
7 votes
Vous pouvez considérer l'édition 2020 de cette réponse. stackoverflow.com/a/17016257/1219006 ce qui semble être la meilleure solution actuellement pour Python 3.6(cpython)-7(tous les pythons)+
list(dict.fromkeys(items))