Python
Lists
Reverse Order
Programming
Range Function

Print a list in reverse order with range?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Printing a list in reverse order is a common task in Python, whether you are processing log entries, implementing undo functionality, or simply displaying data from newest to oldest. Python provides several approaches to iterate backward through a list, each with different trade-offs in memory usage, performance, and readability.

Using range() with a Negative Step

The range() function accepts three arguments: start, stop, and step. By setting the step to -1, you iterate from the last index down to zero. This approach works directly with indices and does not create a copy of the list.

python
1my_list = ["apple", "banana", "cherry", "date", "elderberry"]
2
3for i in range(len(my_list) - 1, -1, -1):
4    print(my_list[i])

Breaking down the arguments:

  • len(my_list) - 1 is the index of the last element (4 in this case).
  • The second -1 means the range stops before reaching -1, so it includes index 0.
  • The third -1 is the step, moving backward one position at a time.

This method is memory-efficient because range() produces indices lazily without allocating a new list. It is a good choice when you need the index value during iteration, for example to modify elements in place.

Using reversed()

The built-in reversed() function returns a reverse iterator over the original list. It does not create a copy of the data, making it both memory-efficient and readable.

python
1my_list = ["apple", "banana", "cherry", "date", "elderberry"]
2
3for item in reversed(my_list):
4    print(item)

Under the hood, reversed() calls the __reversed__() method on the list object, which yields elements from the last index to the first. Since it returns an iterator, it consumes O(1) extra memory regardless of list size.

If you need a reversed list object rather than just iterating, you can wrap it in list():

python
reversed_copy = list(reversed(my_list))
print(reversed_copy)
# ['elderberry', 'date', 'cherry', 'banana', 'apple']

Using Slice Notation [::-1]

Python slice notation supports a step parameter. Using [::-1] creates a new list with elements in reverse order.

python
1my_list = ["apple", "banana", "cherry", "date", "elderberry"]
2
3for item in my_list[::-1]:
4    print(item)

This approach is concise and idiomatic. However, it creates a full copy of the list in memory, which matters for large datasets. For a list with one million elements, [::-1] allocates a second list of one million references.

You can also assign the reversed slice to a variable for later use:

python
reversed_list = my_list[::-1]

Using a While Loop

A manual while loop gives you full control over the iteration process. This can be useful when you need custom skip logic or early termination.

python
1my_list = ["apple", "banana", "cherry", "date", "elderberry"]
2
3index = len(my_list) - 1
4while index >= 0:
5    print(my_list[index])
6    index -= 1

While this is the most verbose approach, it allows you to adjust the step size, skip certain indices, or break out of the loop based on conditions that are harder to express with range() or reversed().

Performance Comparison

Each method has different characteristics when applied to large lists:

python
1import timeit
2
3big_list = list(range(1_000_000))
4
5# range() with negative step
6t1 = timeit.timeit(lambda: [big_list[i] for i in range(len(big_list) - 1, -1, -1)], number=10)
7
8# reversed()
9t2 = timeit.timeit(lambda: [x for x in reversed(big_list)], number=10)
10
11# slice [::-1]
12t3 = timeit.timeit(lambda: big_list[::-1], number=10)
13
14print(f"range():    {t1:.4f}s")
15print(f"reversed(): {t2:.4f}s")
16print(f"slice:      {t3:.4f}s")

In typical benchmarks, [::-1] is the fastest because it runs at C level internally, but it uses the most memory. reversed() strikes a balance between speed and memory, while range() with indexing is slightly slower due to repeated __getitem__ calls.

Common Pitfalls

  • Off-by-one errors in range(): Forgetting that range() excludes the stop value leads to skipping the first element. The stop must be -1, not 0.
  • Mutating during iteration: Modifying a list while iterating over it with reversed() or slicing can produce unexpected results or errors.
  • Memory overhead with slicing: Using [::-1] on very large lists doubles memory consumption because it creates a full copy.
  • Confusing reversed() with .reverse(): The reversed() function returns a new iterator and leaves the original list unchanged, while list.reverse() modifies the list in place and returns None.
  • Using range() when you do not need the index: If you only need the element values, reversed() is cleaner and avoids index bookkeeping.

Summary

  • Use range(len(lst) - 1, -1, -1) when you need both the index and the value during reverse iteration.
  • Use reversed() for a memory-efficient, readable reverse iterator that does not copy the list.
  • Use [::-1] for a concise one-liner that produces a new reversed list, accepting the memory cost.
  • Use a while loop when you need custom control flow such as variable step sizes or conditional breaks.
  • For most applications, reversed() is the recommended default because it combines low memory usage with clear intent.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.