Le meilleur moyen de créer une liste «inversée» en Python?

89

En Python, quel est le meilleur moyen de créer une nouvelle liste dont les éléments sont les mêmes que ceux d'une autre liste, mais dans l'ordre inverse? (Je ne veux pas modifier la liste existante en place.)

Voici une solution qui m'est venue à l'esprit:

new_list = list(reversed(old_list))

Il est également possible de dupliquer old_listpuis d'inverser le duplicata en place:

new_list = list(old_list) # or `new_list = old_list[:]`
new_list.reverse()

Y a-t-il une meilleure option que j'ai négligée? Sinon, y a-t-il une raison impérieuse (comme l'efficacité) d'utiliser l'une des approches ci-dessus plutôt que l'autre?

davidchambers
la source

Réponses:

205
newlist = oldlist[::-1]

Le [::-1]tranchage (que ma femme Anna aime appeler "le smiley martien" ;-) signifie: trancher toute la séquence, avec un pas de -1, c'est-à-dire à l'envers. Cela fonctionne pour toutes les séquences.

Notez que ceci ( et les alternatives que vous avez mentionnées) équivaut à une "copie superficielle", c'est-à-dire: si les éléments sont mutables et que vous appelez des mutateurs sur eux, les mutations des éléments contenus dans la liste d'origine sont également dans les éléments de la liste inversée, et vice versa. Si vous devez éviter cela, un copy.deepcopy(bien que toujours une opération potentiellement coûteuse), suivi dans ce cas par un .reverse, est la seule bonne option.

Alex Martelli
la source
Oh mon! C'est élégant. Jusqu'à il y a quelques jours, je n'avais pas réalisé qu'il était possible d'inclure une "étape" lors du tranchage; maintenant je me demande comment je me suis débrouillé sans ça! Merci Alex. :)
davidchambers
1
Merci également d'avoir mentionné que cela produit une copie superficielle. C'est tout ce dont j'ai besoin, donc je suis sur le point d'ajouter un smiley martien à mon code.
davidchambers
13
Cela semble vraiment beaucoup plus magique et beaucoup moins lisible que la suggestion originale, list(reversed(oldlist)). Autre qu'un micro-optimisation mineurs, est - il une raison de préférer [::-1]à reversed()?
Brian Campbell
@BrianCampbell: Qu'est-ce que c'est "magique"? Si vous comprenez du tout le tranchage, c'est tout à fait logique. Si vous ne comprenez pas le tranchage… eh bien, vous devriez vraiment l'apprendre assez tôt dans votre carrière Python. Bien sûr, cela reversedprésente un énorme avantage lorsque vous n'avez pas besoin de la liste, car elle ne gaspille pas de mémoire ni de temps à construire. Mais quand vous avez besoin de la liste, en utilisant au [::-1]lieu de list(reversed())est analogue à l' aide d' un [listcomp]lieu de list(genexpr).
abarnert le
10
@abarnert Dans le code avec lequel je travaille, je vois si rarement l'argument de la troisième tranche, si j'en vois une utilisation, je dois chercher ce que cela signifie. Une fois que je le fais, il n'est toujours pas évident que les valeurs de début et de fin par défaut soient permutées lorsque l'étape est négative. En un coup d'œil, sans chercher la signification du troisième argument, je suppose que cela [::-1]signifie supprimer le dernier élément d'une liste, plutôt que de l'inverser. reversed(list)indique exactement ce qu'il fait; il énonce son intention, au sens «Explicite vaut mieux qu'implicite», «La lisibilité compte» et «Sparse vaut mieux que dense».
Brian Campbell
56

Maintenant allons timeit. Indice: celui d' Alex [::-1]est le plus rapide :)

$ p -m timeit "ol = [1, 2, 3]; nl = list(reversed(ol))"
100000 loops, best of 3: 2.34 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = list(ol); nl.reverse();"
1000000 loops, best of 3: 0.686 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = ol[::-1];"
1000000 loops, best of 3: 0.569 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = [i for i in reversed(ol)];"
1000000 loops, best of 3: 1.48 usec per loop


$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 44.7 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(ol); nl.reverse();"
10000 loops, best of 3: 27.2 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = ol[::-1];"
10000 loops, best of 3: 24.3 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = [i for i in reversed(ol)];"
10000 loops, best of 3: 155 usec per loop

Mise à jour: Ajout de la méthode de compilation de liste suggérée par inspectorG4dget. Je vais laisser les résultats parler d'eux-mêmes.

Sam Dolan
la source
8
Juste une note - c'est exact pour créer une liste de copie inversée mais inversée est encore plus efficace pour l'itération alors[::-1]
Tadhg McDonald-Jensen
7

Ajustements

Cela vaut la peine de fournir une référence / un ajustement de base pour les calculs de timeit par sdolan qui montrent les performances de `` inversé '' sans la list()conversion souvent inutile . Cette list()opération ajoute 26 usecs supplémentaires au runtime et n'est nécessaire que dans le cas où un itérateur est inacceptable.

Résultats:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Calculs:

# I ran this set of 100000 and came up with 11.2, twice:
python -m timeit "ol = [1, 2, 3]*1000; nl = reversed(ol)"
100000 loops, best of 3: 11.2 usec per loop

# This shows the overhead of list()
python -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 37.1 usec per loop

# This is the result for reverse via -1 step slices
python -m timeit "ol = [1, 2, 3]*1000;nl = ol[::-1]"
10000 loops, best of 3: 23.6 usec per loop

Conclusions:

La conclusion de ces tests est reversed()plus rapide que la tranche [::-1]de 12,4 usecs

mekarpeles
la source
15
reverse () renvoie un objet itérateur qui est évalué paresseusement, donc je pense que ce n'est pas une comparaison juste avec la notation de découpage [:: - 1] en général.
irisé
1
Même dans le cas où un itérateur peut être utilisé directement, comme par exemple ''.join(reversed(['1','2','3'])), la méthode de tranche est encore> 30% plus rapide.
dansalmo
1
Pas étonnant que vous ayez obtenu le même résultat lors des 2 premiers tests: ils sont identiques!
MestreLion
Mes résultats ressemblent plus à ceci. ol [:: - 1] prend environ deux fois plus de temps que list (inversé (ol)). La méthode ol [:: - 1] prend moins de caractères à écrire. Cependant, la liste (inversée (ol)) est probablement plus lisible pour les programmeurs python débutants et elle est plus rapide sur ma machine.
dhj