105

In Python, what is the best way to create a new list whose items are the same as those of some other list, but in reverse order? (I don't want to modify the existing list in place.)

Here is one solution that has occurred to me:

new_list = list(reversed(old_list))

It's also possible to duplicate old_list then reverse the duplicate in place:

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

Is there a better option that I've overlooked? If not, is there a compelling reason (such as efficiency) to use one of the above approaches over the other?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
davidchambers
  • 23,918
  • 16
  • 76
  • 105

3 Answers3

231
newlist = oldlist[::-1]

The [::-1] slicing (which my wife Anna likes to call "the Martian smiley";-) means: slice the whole sequence, with a step of -1, i.e., in reverse. It works for all sequences.

Note that this (and the alternatives you mentioned) is equivalent to a "shallow copy", i.e.: if the items are mutable and you call mutators on them, the mutations in the items held in the original list are also in the items in the reversed list, and vice versa. If you need to avoid that, a copy.deepcopy (while always a potentially costly operation), followed in this case by a .reverse, is the only good option.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Oh my! That _is_ elegant. Until a few days ago I didn't realize that it's possible to include a "step" when slicing; now I'm wondering how I ever got by without it! Thanks, Alex. :) – davidchambers Sep 14 '10 at 02:13
  • 1
    Thanks, too, for mentioning that this produces a shallow copy. That's all I need, though, so I'm about to add a Martian smiley to my code. – davidchambers Sep 14 '10 at 02:26
  • 15
    This really seems a lot more magic and a lot less readable than the original suggestion, `list(reversed(oldlist))`. Other than a minor micro-optimization, is there any reason to prefer `[::-1]` to `reversed()`? – Brian Campbell Aug 07 '13 at 17:35
  • 2
    @BrianCampbell: What's "magic" about it? If you understand slicing at all, it makes total sense. If you don't understand slicing… well, you really should learn it pretty early in your Python career. Of course `reversed` has a huge advantage when you _don't_ need the list, because it doesn't waste memory or up-front time building it. But when you _do_ need the list, using `[::-1]` instead of `list(reversed())` is analogous to using a `[listcomp]` instead of `list(genexpr)`. – abarnert Sep 11 '14 at 07:48
  • 14
    @abarnert In code I work with, I so rarely see the third slice argument, if I see a use of it I have to look up what it means. Once I do, it's still not obvious that the default start and end values are swapped when the step is negative. At a quick glance, without looking up the meaning of the third argument, I might guess that `[::-1]` means removing the last element of a list, rather than reversing it. `reversed(list)` states exactly what it's doing; it spells out its intent, in the sense of "Explicit is better than implicit", "Readability counts", and "Sparse is better than dense". – Brian Campbell Sep 11 '14 at 12:55
65

Now let's timeit. Hint: Alex's [::-1] is fastest :)

$ 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

Update: Added list comp method suggested by inspectorG4dget. I'll let the results speak for themselves.

Sam Dolan
  • 31,966
  • 10
  • 88
  • 84
11

Adjustments

It's worth providing a baseline benchmark/adjustment for the timeit calculations by sdolan which show the performance of 'reversed' without the often unnecessary list() conversion. This list() operation adds an additional 26 usecs to the runtime and is only needed in the event an iterator is unacceptable.

Results:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

Calculations:

# 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:

The conclusion of these tests is reversed() is faster than the slice [::-1] by 12.4 usecs

Ivan
  • 34,531
  • 8
  • 55
  • 100
mekarpeles
  • 365
  • 3
  • 9
  • 16
    reversed() returns an iterator object which is lazy-evaluated, so I think it is not a fair comparison to the slicing notation [::-1] in general. – iridescent Aug 06 '13 at 03:06
  • 1
    Even in a case where an iterator can be used directly, such as `''.join(reversed(['1','2','3']))`, the slice method is still >30% faster. – dansalmo Nov 25 '13 at 21:28
  • 1
    No wonder why you got the same result from the first 2 tests: they are identical! – MestreLion Aug 25 '14 at 21:59
  • My results more closely resemble this. ol[::-1] takes about twice as long as list(reversed(ol)). The ol[::-1] method takes fewer characters to write. However, list(reversed(ol)) is probably more readable for beginner python programmers and it is faster on my machine. – dhj Apr 28 '15 at 14:40