How do I iterate over a list in reverse in Python?
-
Possible duplicate of stackoverflow.com/questions/931092/reverse-a-string-in-pythonᴇɴᴅᴇʀᴍᴀɴ– ᴇɴᴅᴇʀᴍᴀɴ2022-06-24 22:24:18 +00:00Commented Jun 24, 2022 at 22:24
-
From what I can tell, the question was only ever intended to be about iteration. However, the day after it was asked, someone tried to fix the (rather poor) title - and misunderstood, giving it the title "How to reverse a list?" (which means changing around the order of the elements, not looking at the elements in a different order). The question then attracted answers for both problems, so now we have a rather confused canonical. I at least added to the title to indicate that both purposes are served here.Karl Knechtel– Karl Knechtel2022-08-11 08:15:41 +00:00Commented Aug 11, 2022 at 8:15
18 Answers
To get a new reversed list, apply the reversed function and collect the items into a list:
>>> xs = [0, 10, 20, 40]
>>> list(reversed(xs))
[40, 20, 10, 0]
To iterate backwards through a list:
>>> xs = [0, 10, 20, 40]
>>> for x in reversed(xs):
... print(x)
40
20
10
0
8 Comments
reverse might be faster though, if you don't need to cast to list afterwards.reversed() instead of slicing? Read the Zen of Python, rule number 7: Readability counts!>>> xs = [0, 10, 20, 40]
>>> xs[::-1]
[40, 20, 10, 0]
Extended slice syntax is explained here. See also, documentation.
9 Comments
set is iterable, but not subscriptable.L[::-1] and it won't take value for start, stop as slice method is defined with. Try with L[4:0:-1] and you will see problem.start and stop work. To do what it looks like you're trying to do (return the first 4 values in reverse order), you would use this: L[3::-1] - start at index 3 and go back 1 (-1) index until you get to the beginning.Use list.reverse to reverse a list in-place:
>>> xs = [0, 10, 20, 40]
>>> xs.reverse()
>>> xs
[40, 20, 10, 0]
Use slices to create a new list with the items in reverse order:
>>> xs[::-1]
[40, 20, 10, 0]
11 Comments
L=L[::-1] to actually reverse the list otherwise you're only returning the values in reverseb = l[-n:] b.reverse() l = b + l[:len(l) - n]Summary of Reverse Methods
There are three different built-in ways to reverse a list. Which method is best depends on whether you need to:
- Reverse an existing list in-place (altering the original list variable)
- Best solution is
object.reverse()method
- Best solution is
- Create an iterator of the reversed list (because you are going to feed it to a for-loop, a generator, etc.)
- Best solution is
reversed(object)which creates the iterator
- Best solution is
- Create a copy of the list, just in the reverse order (to preserve the original list)
- Best solution is using slices with a -1 step size:
object[::-1]
- Best solution is using slices with a -1 step size:
From a speed perspective, it is best to use the above built-in functions to reverse a list. For reversing, they are 2 to 8 times faster on short lists (10 items), and up to ~300+ times faster on long lists compared to a manually-created loop or generator. This makes sense - they are written in a native language (i.e. C), have experts creating them, scrutiny, and optimization. They are also less prone to defects and more likely to handle edge and corner cases.
Test Script
Put all the code snippets in this answer together to make a script that will run the different ways of reversing a list that are described below. It will time each method while running it 100,000 times. The results are shown in the last section for lists of length 2, 10, and 1000 items.
from timeit import timeit
from copy import copy
def time_str_ms(t):
return '{0:8.2f} ms'.format(t * 1000)
Method 1: Reverse in place with obj.reverse()
If the goal is just to reverse the order of the items in an existing list, without looping over them or getting a copy to work with, use the <list>.reverse() function. Run this directly on a list object, and the order of all items will be reversed:
Note that the following will reverse the original variable that is given, even though it also returns the reversed list back. i.e. you can create a copy by using this function output. Typically, you wouldn't make a function for this, but the timing script requires it.
We test the performance of this two ways - first just reversing a list in-place (changes the original list), and then copying the list and reversing it afterward to see if that is the fastest way to create a reversed copy compared to the other methods.
def rev_in_place(mylist):
mylist.reverse()
return mylist
def rev_copy_reverse(mylist):
a = copy(mylist)
a.reverse()
return a
Method 2: Reverse a list using slices obj[::-1]
The built-in index slicing method allows you to make a copy of part of any indexed object.
- It does not affect the original object
- It builds a full list, not an iterator
The generic syntax is: <object>[first_index:last_index:step]. To exploit slicing to create a simple reversed list, use: <list>[::-1]. When leaving an option empty, it sets them to defaults of the first and last element of the object (reversed if the step size is negative).
Indexing allows one to use negative numbers, which count from the end of the object's index backwards (i.e. -2 is the second to last item). When the step size is negative, it will start with the last item and index backward by that amount.
def rev_slice(mylist):
a = mylist[::-1]
return a
Method 3: Reverse a list with the reversed(obj) iterator function
There is a reversed(indexed_object) function:
- This creates a reverse index iterator, not a list. Great if you are feeding it to a loop for better performance on large lists
- This creates a copy and does not affect the original object
Test with both a raw iterator, and creating a list from the iterator.
def reversed_iterator(mylist):
a = reversed(mylist)
return a
def reversed_with_list(mylist):
a = list(reversed(mylist))
return a
Method 4: Reverse list with Custom/Manual indexing
As the timing shows, creating your own methods of indexing is a bad idea. Use the built-in methods unless you really do need to do something custom. This simply means learning the built-in methods.
That said, there is not a huge penalty with smaller list sizes, but when you scale up the penalty becomes tremendous. The code below could be optimized, I'm sure, but it can't ever match the built-in methods as they are directly implemented in a native language.
def rev_manual_pos_gen(mylist):
max_index = len(mylist) - 1
return [ mylist[max_index - index] for index in range(len(mylist)) ]
def rev_manual_neg_gen(mylist):
## index is 0 to 9, but we need -1 to -10
return [ mylist[-index-1] for index in range(len(mylist)) ]
def rev_manual_index_loop(mylist):
a = []
reverse_index = len(mylist) - 1
for index in range(len(mylist)):
a.append(mylist[reverse_index - index])
return a
def rev_manual_loop(mylist):
a = []
reverse_index = len(mylist)
for index, _ in enumerate(mylist):
reverse_index -= 1
a.append(mylist[reverse_index])
return a
Timing each method
Following is the rest of the script to time each method of reversing. It shows reversing in place with obj.reverse() and creating the reversed(obj) iterator are always the fastest, while using slices is the fastest way to create a copy.
It also proves not to try to create a way of doing it on your own unless you have to!
loops_to_test = 100000
number_of_items = 10
list_to_reverse = list(range(number_of_items))
if number_of_items < 15:
print("a: {}".format(list_to_reverse))
print('Loops: {:,}'.format(loops_to_test))
# List of the functions we want to test with the timer, in print order
fcns = [rev_in_place, reversed_iterator, rev_slice, rev_copy_reverse,
reversed_with_list, rev_manual_pos_gen, rev_manual_neg_gen,
rev_manual_index_loop, rev_manual_loop]
max_name_string = max([ len(fcn.__name__) for fcn in fcns ])
for fcn in fcns:
a = copy(list_to_reverse) # copy to start fresh each loop
out_str = ' | out = {}'.format(fcn(a)) if number_of_items < 15 else ''
# Time in ms for the given # of loops on this fcn
time_str = time_str_ms(timeit(lambda: fcn(a), number=loops_to_test))
# Get the output string for this function
fcn_str = '{}(a):'.format(fcn.__name__)
# Add the correct string length to accommodate the maximum fcn name
format_str = '{{fx:{}s}} {{time}}{{rev}}'.format(max_name_string + 4)
print(format_str.format(fx=fcn_str, time=time_str, rev=out_str))
Timing Results
NOTE: Below results run on Python 3.7.9
The results show that scaling works best with the built-in methods best suited for a particular type of reversing. In other words, as the object element count increases, the built-in methods outpace the other methods by even more.
The built-in method that directly achieves what you need does better than stringing things together. i.e. slicing is best if you need a copy of the reversed list - it's faster than creating a duplicate list from list(reversed(obj)) function, and faster than making a copy of the list and then doing an in-place obj.reverse(), but never by more than double the speed. Meanwhile - custom methods can take orders of magnitude longer with large lists.
For scaling, with a 1000 item list, the reversed(<list>) function call takes ~30 ms to setup the iterator, reversing in-place takes just ~55 ms, using the slice method takes ~210 ms to create a copy of the full reversed list, but the quickest manual method I made took ~8400 ms.
With 2 items in the list:
a: [0, 1]
Loops: 100,000
rev_in_place(a): 24.70 ms | out = [1, 0]
reversed_iterator(a): 30.48 ms | out = <list_reverseiterator object at 0x0000020242580408>
rev_slice(a): 31.65 ms | out = [1, 0]
rev_copy_reverse(a): 63.42 ms | out = [1, 0]
reversed_with_list(a): 48.65 ms | out = [1, 0]
rev_manual_pos_gen(a): 98.94 ms | out = [1, 0]
rev_manual_neg_gen(a): 88.11 ms | out = [1, 0]
rev_manual_index_loop(a): 87.23 ms | out = [1, 0]
rev_manual_loop(a): 79.24 ms | out = [1, 0]
With 10 items in the list:
rev_in_place(a): 23.39 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_iterator(a): 30.23 ms | out = <list_reverseiterator object at 0x00000290A3CB0388>
rev_slice(a): 36.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_copy_reverse(a): 64.67 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_with_list(a): 50.77 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_pos_gen(a): 162.83 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_neg_gen(a): 167.43 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_index_loop(a): 152.04 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_loop(a): 183.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
And with 1000 items in the list:
rev_in_place(a): 56.37 ms
reversed_iterator(a): 30.47 ms
rev_slice(a): 211.42 ms
rev_copy_reverse(a): 295.74 ms
reversed_with_list(a): 418.45 ms
rev_manual_pos_gen(a): 8410.01 ms
rev_manual_neg_gen(a): 11054.84 ms
rev_manual_index_loop(a): 10543.11 ms
rev_manual_loop(a): 15472.66 ms
Note: Dec, 2023 with Python 3.11 performance improvements
I ran a quick comparison of Python 3.7 to 3.11 (on the same PC just now) with 1 million cycles to see what difference the the 3.11 performance improvements would make. Python 3.11 was 26.5% faster on average, roughly the same for manual as for built in methods of reversing lists.
Comments
I find (contrary to some other suggestions) that l.reverse() is by far the fastest way to reverse a long list in Python 3 and 2. I'd be interested to know if others can replicate these timings.
l[::-1] is probably slower because it copies the list prior to reversing it. Adding the list() call around the iterator made by reversed(l) must add some overhead. Of course if you want a copy of the list or an iterator then use those respective methods, but if you want to just reverse the list then l.reverse() seems to be the fastest way.
Functions
def rev_list1(l):
return l[::-1]
def rev_list2(l):
return list(reversed(l))
def rev_list3(l):
l.reverse()
return l
List
l = list(range(1000000))
Python 3.5 timings
timeit(lambda: rev_list1(l), number=1000)
# 6.48
timeit(lambda: rev_list2(l), number=1000)
# 7.13
timeit(lambda: rev_list3(l), number=1000)
# 0.44
Python 2.7 timings
timeit(lambda: rev_list1(l), number=1000)
# 6.76
timeit(lambda: rev_list2(l), number=1000)
# 9.18
timeit(lambda: rev_list3(l), number=1000)
# 0.46
3 Comments
list.reverse is the fastest, because it reverses in placelist.reverse() is fastest, but you're penalizing reversed (which is best used when you don't want a new list, just to iterate an existing list in reverse order without mutating the original), and the slice (which also avoids mutating the original list, and is typically faster than reversed when the input is small). Yes, if you don't need the copy, anything that copies is more expensive, but a lot of the time, you don't want to mutate the original value.reversed still loses to list.reverse() even so, but given it doesn't mutate the input list, it's better in many cases. The loss for reversed is small (~1/6th longer than list.reverse()).Another solution would be to use numpy.flip for this
import numpy as np
array = [0, 10, 20, 40]
list(np.flip(array))
[40, 20, 10, 0]
1 Comment
You can also use the bitwise complement of the array index to step through the array in reverse:
>>> array = [0, 10, 20, 40]
>>> [array[~i] for i, _ in enumerate(array)]
[40, 20, 10, 0]
Whatever you do, don't do it this way ;)
Comments
If you want to store the elements of reversed list in some other variable, then you can use revArray = array[::-1] or revArray = list(reversed(array)).
But the first variant is slightly faster:
z = range(1000000)
startTimeTic = time.time()
y = z[::-1]
print("Time: %s s" % (time.time() - startTimeTic))
f = range(1000000)
startTimeTic = time.time()
g = list(reversed(f))
print("Time: %s s" % (time.time() - startTimeTic))
Output:
Time: 0.00489711761475 s
Time: 0.00609302520752 s
2 Comments
timeit.range(1000000) may not be a list at all, but rather a range object, with its own __reversed__ method that presumably behaves differently from that of a list.def reverse(my_list):
L = len(my_list)
for i in range(L/2):
my_list[i], my_list[L-i - 1] = my_list[L-i-1], my_list[i]
return my_list
1 Comment
// floor division operator.You could always treat the list like a stack just popping the elements off the top of the stack from the back end of the list. That way you take advantage of first in last out characteristics of a stack. Of course you are consuming the 1st array. I do like this method in that it's pretty intuitive in that you see one list being consumed from the back end while the other is being built from the front end.
>>> l = [1,2,3,4,5,6]; nl=[]
>>> while l:
nl.append(l.pop())
>>> print nl
[6, 5, 4, 3, 2, 1]
Comments
Reversing in-place by switching references of opposite indices:
>>> l = [1,2,3,4,5,6,7]
>>> for i in range(len(l)//2):
... l[i], l[-1-i] = l[-1-i], l[i]
...
>>> l
[7, 6, 5, 4, 3, 2, 1]
2 Comments
>>> l = [1, 2, 3, 4, 5]
>>> print(reduce(lambda acc, x: [x] + acc, l, []))
[5, 4, 3, 2, 1]
1 Comment
l[::-1], and at the same time much less legible. Functional programming in Python is sadly rather slow.This class uses Python magic methods and iterators for reversing, and reverses a list:
class Reverse(object):
""" Builds a reverse method using magic methods """
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
REV_INSTANCE = Reverse([0, 10, 20, 40])
iter(REV_INSTANCE)
rev_list = []
for i in REV_INSTANCE:
rev_list.append(i)
print(rev_list)
Output
[40, 20, 10, 0]
1 Comment
reversed.