reversed() returns an iterator that walks a sequence backwards. It does not create a reversed copy, which is why printing it shows an object rather than your data. On a range it is especially efficient — no values are generated until you ask for them, so reversing a range of ten million costs nothing.
The basics#
numbers = [1, 2, 3, 4, 5]
print(reversed(numbers)) # <list_reverseiterator object at 0x...>
print(list(reversed(numbers))) # [5, 4, 3, 2, 1]
for n in reversed(numbers):
print(n) # 5 4 3 2 1
The first line is the thing that confuses people. reversed() is lazy — it produces values only as you consume them. In a for loop that is exactly what you want; to see or store the values, wrap it in list().
Reversing a range#
for i in reversed(range(5)):
print(i) # 4 3 2 1 0
for i in reversed(range(1, 11)):
print(i) # 10 down to 1
for i in reversed(range(0, 20, 2)):
print(i) # 18 16 14 ... 0
The equivalent with a negative step:
for i in range(4, -1, -1):
print(i) # 4 3 2 1 0
Both are correct and produce identical output. reversed(range(5)) is easier to read and much harder to get wrong — the negative-step version needs the stop value to be one past where you want to finish, which is a classic off-by-one source.
range(5, 0, -1) # 5 4 3 2 1 - does NOT include 0
range(4, -1, -1) # 4 3 2 1 0 - does include 0
Three ways to reverse#
| Method | Returns | Changes the original | Memory |
|---|---|---|---|
reversed(seq) |
an iterator | no | constant |
seq[::-1] |
a new sequence | no | a full copy |
list.reverse() |
None |
yes, in place | constant |
numbers = [1, 2, 3]
a = list(reversed(numbers)) # [3, 2, 1], numbers unchanged
b = numbers[::-1] # [3, 2, 1], numbers unchanged
c = numbers.reverse() # c is None, numbers is now [3, 2, 1]
Which to choose#
- Looping backwards? Use
reversed(). It allocates nothing. - Need a reversed list or string to keep? Use slicing.
- Want to reverse a list permanently? Use
.reverse().
big = list(range(10_000_000))
for n in reversed(big): # no copy
if n < 5: break
copy = big[::-1] # allocates a second 10-million list
Strings#
text = "hello"
print(text[::-1]) # "olleh" - the usual way
print("".join(reversed(text))) # "olleh" - same result, slower
Slicing is the idiomatic choice for strings. The reversed version has to build a list of characters and join them, which does more work for the same answer.
One caution: neither handles combining characters or emoji sequences correctly, because they reverse code points rather than what a reader sees as a character.
print("café"[::-1]) # works - the accent is a single code point
print("👨👩👧"[::-1]) # breaks - a multi-code-point sequence
What can be reversed#
reversed([1, 2, 3]) # list - fine
reversed((1, 2, 3)) # tuple - fine
reversed("abc") # string - fine
reversed(range(5)) # range - fine
reversed({1: "a", 2: "b"}) # dict - fine in Python 3.8+, gives keys
reversed({1, 2, 3}) # TypeError - sets have no order
reversed(x for x in [1, 2]) # TypeError - generators cannot be reversed
A sequence must know its length and support indexing to be reversible. Sets and generators know neither, so you have to materialise them first:
gen = (x * 2 for x in range(5))
print(list(reversed(list(gen)))) # [8, 6, 4, 2, 0]
Your own class#
class Playlist:
def __init__(self, tracks):
self.tracks = tracks
def __len__(self):
return len(self.tracks)
def __getitem__(self, index):
return self.tracks[index]
p = Playlist(["a", "b", "c"])
print(list(reversed(p))) # ['c', 'b', 'a']
Defining __len__ and __getitem__ is enough — Python builds the reverse iterator from them. Define __reversed__ only if you can do it more efficiently than that default.
The consumed-iterator bug#
r = reversed([1, 2, 3])
print(list(r)) # [3, 2, 1]
print(list(r)) # [] - already exhausted
print(sum(r)) # 0
An iterator produces its values once. After the first list() walked through it, there is nothing left — and no error, just an empty result. If you need the values twice, store them.
Combining with other functions#
names = ["ana", "sam", "kim"]
for i, name in enumerate(reversed(names)):
print(i, name) # 0 kim, 1 sam, 2 ana
# Index counting down instead
for i, name in zip(reversed(range(len(names))), reversed(names)):
print(i, name) # 2 kim, 1 sam, 0 ana
# Sorted descending - use sorted, not reversed
print(sorted(names, reverse=True))
That last line is worth noting: reversed(sorted(x)) and sorted(x, reverse=True) give the same result, but the second is clearer and does not build an intermediate list.
Questions people ask#
Why does printing reversed() show an object?
Because it is an iterator, not a list. It has produced nothing yet. Wrap it in list() to see the values.
Is slicing faster than reversed()?
For building a full reversed copy, slicing is faster. For iterating, reversed() wins because it allocates nothing.
Can I reverse a dictionary?
Since Python 3.8, reversed(d) gives the keys in reverse insertion order, and reversed(d.items()) gives the pairs. Before that, convert to a list first.
How do I reverse a list of lists?
reversed() reverses the outer list only. To reverse each inner list as well: [list(reversed(inner)) for inner in reversed(outer)].
Where to go next#
- Python lists explained — slicing and list methods in full.
- Python for loops explained — range, enumerate and zip.
- Generators and iterators — why laziness matters.