25

What is typically regarded as more Pythonic/better/faster to use, the reverse method or the reversed built-in function?

Both in action:

_list = list(xrange(4))

print _list

rlist = list(reversed(_list))

print rlist

_list.reverse()

print _list
rectangletangle
  • 50,393
  • 94
  • 205
  • 275

8 Answers8

55

foo.reverse() actually reverses the elements in the container. reversed() doesn't actually reverse anything, it merely returns an object that can be used to iterate over the container's elements in reverse order. If that's what you need, it's often faster than actually reversing the elements.

kindall
  • 178,883
  • 35
  • 278
  • 309
17

There seems to be a great difference. I really thought it's the other way round. Why is rearranging the values in a list faster than creating a new one from an iterator ?

from decorators import bench

_list = range(10 ** 6)

@ bench
def foo():
  list(reversed(_list))

@ bench
def bar():
  _list.reverse()

foo()
bar()

print foo.time
print bar.time

0.167278051376
0.0122621059418

Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • The memory allocation itself shouldn't take too long; IIRC the list is a single contiguous allocation (PyObject**). More likely the difference is because `list(reversed(_list))`, even though the `list` constructor is C code, must go through the `reverseiterator`'s API instead of just swapping pointers in a tight C loop. – Karl Knechtel Jul 25 '11 at 06:47
  • If they have to check for `listreverseiterator` then they have to check for everything... eventually it gets kind of unwieldy, and slows down short copies. Besides, the intended-to-be-common case for `reversed` is when you're going to iterate anyway and don't need to create a temporary. – Karl Knechtel Jul 26 '11 at 01:00
  • 2
    If you do something with the lists, the picture changes. Comparing `for i in reversed(_list):...` and `_list.reverse();for i in _list:...`, the _former_ is a little faster. – Weidenrinde Aug 08 '13 at 20:56
15

Depends on whether you want to reverse the list in-place (i.e. change the list) or not. No other real difference.

Often using reversed leads to nicer code.

Katriel
  • 120,462
  • 19
  • 136
  • 170
4

Without knowing real stats about performance, _list.reverse() modifies the list itself, whereas reversed(_list) returns an iterator ready to traverse the list in reversed order. That's a big difference itself.

If that's not a problem, object.reverse() seems more readable to me, but maybe you have specific speed requirements. And if reverse() does not belong to 80% of software that's consuming resources, I wouldn't bother (as a general rule of thumb).

elitalon
  • 9,191
  • 10
  • 50
  • 86
2
  • _list.reverse() does an in-place reversal and does not return a value
  • reversed(_list) does not change _list, but returns a reverse iterable object
  • _list[::-1] does not change _list, but returns reversed slice

example:

_list = [1,2,3]
ret1 = list(reversed(_list))
ret2 = _list[::-1] #reverse order slice
ret3 = _list.reverse() #no value set in ret3
print('ret1,ret2,ret3,_list:',ret1,ret2,ret3,_list)

_list = [1,2,3]
for x in reversed(_list):
    print(x)

output:

ret1,ret2,ret3,_list: [3, 2, 1] [3, 2, 1] None [3, 2, 1]
3
2
1
JayS
  • 2,057
  • 24
  • 16
0

It is always better to use reversed() if eventually you are going to modify the list in iterator making list immutable and working with immutable data is always better especially when your doing functional programming.

finepax007
  • 651
  • 3
  • 8
  • 16
0

Expanding on @Niklas R answer:

import timeit

print('list.reverse() - real-list', timeit.timeit('_list.reverse()', '_list = list(range(1_000))'))
print('list.reverse() - iterator', timeit.timeit('_list = range(1_000); list(_list).reverse()'))  # can't really use .reverse() since you need to cast it first
print('reversed() - real-list', timeit.timeit('list(reversed(_list))', '_list = list(range(1_000))'))
print('reversed() - iterator', timeit.timeit('_list = range(1_000); list(reversed(_list))'))
print('list-comprehension - real-list', timeit.timeit('_list[::-1]', '_list = list(range(1_000))'))
print('list-comprehension - iterator', timeit.timeit('_list = range(1_000); _list[::-1]'))

Results:

list.reverse() - real-list 0.29828099999576807
list.reverse() - iterator 11.078685999964364  # can't really use .reverse() since you need to cast it first
reversed() - real-list 3.7131450000451878
reversed() - iterator 12.048991999938153
list-comprehension - real-list 2.2268580000381917
list-comprehension - iterator 0.4313809999730438

(less is better/faster)

Javier Buzzi
  • 6,296
  • 36
  • 50
0
  • the built-in function reversed(seq) that will return a reverse iterator which is an object representing the stream of data that will return successive items of this steam. Generating this reverse iterator is O(1) in time/space complexity and using it to iterate through the elements of a list will be O(N), O(1) in time/space complexity where N is the length of the list. This is the approach you want to go for if you simply want to iterate on the reversed list without modifying it. This approach gives the best performances in this case.

  • _list.reverse() reverses the list in-place. The operation is O(N), O(1) in time/space complexity as it will have to go through half of the elements of the list to reverse them and it doesn't store the results in a new list. This approach is good if you do NOT need to keep the original list and you have several passes/operations to do on the elements of the reversed list and if you have to save the processed reversed list.

  • Last but not least, using slicing [::-1] creates a new object of the list/a copy in reversed order. The operation is O(N), O(N) in space/time complexity as you have to copy all the elements of the list in the new one and this new list will also consume the same amount of space as the original list. This approach is great if you need to keep the original list and have a reversed copy of it stored in a different object for further processing.

To summarize, depending on your use case you will have to use one of those 3 approaches that have slightly different objectives and completely different performances.

Allan
  • 12,117
  • 3
  • 27
  • 51