Python
Looping
Backward Iteration
Programming
Duplicate

How to loop backwards in python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python provides several ways to loop backwards through a sequence: reversed() for any iterable, range(start, stop, step) with a negative step for index-based iteration, and slice notation [::-1] for creating a reversed copy. The choice depends on whether you need the index, whether you want to avoid creating a copy, and whether you are iterating over a list, string, or range of numbers.

Using reversed()

reversed() returns a reverse iterator without creating a copy:

python
1fruits = ["apple", "banana", "cherry", "date"]
2
3for fruit in reversed(fruits):
4    print(fruit)
5# date
6# cherry
7# banana
8# apple

reversed() works with any sequence (list, tuple, string, range) and any object that implements __reversed__() or __len__() and __getitem__():

python
1# Strings
2for char in reversed("hello"):
3    print(char, end="")  # olleh
4
5# Tuples
6for item in reversed((1, 2, 3)):
7    print(item)  # 3, 2, 1
8
9# Ranges
10for i in reversed(range(5)):
11    print(i)  # 4, 3, 2, 1, 0

Using range() with Negative Step

For iterating over indices in reverse:

python
1# Count down from 10 to 1
2for i in range(10, 0, -1):
3    print(i)
4# 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
5
6# Access list elements by index in reverse
7colors = ["red", "green", "blue", "yellow"]
8for i in range(len(colors) - 1, -1, -1):
9    print(f"{i}: {colors[i]}")
10# 3: yellow
11# 2: blue
12# 1: green
13# 0: red

The range(start, stop, step) parameters for backwards iteration:

  • start: the first value (inclusive)
  • stop: the end value (exclusive) — usually -1 to include index 0
  • step: -1 for stepping backwards by one, -2 for every other, etc.

Using Slice Notation [::-1]

Slice notation creates a reversed copy:

python
1numbers = [1, 2, 3, 4, 5]
2
3# Reversed copy
4for n in numbers[::-1]:
5    print(n)  # 5, 4, 3, 2, 1
6
7# Also works for strings
8text = "Python"
9print(text[::-1])  # nohtyP
10
11# Step of -2 skips every other element
12print(numbers[::-2])  # [5, 3, 1]

Unlike reversed(), slicing creates a new list in memory. For large sequences, reversed() is more memory-efficient.

Using enumerate with reversed

When you need both the index and value:

python
1items = ["a", "b", "c", "d"]
2
3# enumerate + reversed gives reversed values with forward indices
4for i, item in enumerate(reversed(items)):
5    print(f"{i}: {item}")
6# 0: d
7# 1: c
8# 2: b
9# 3: a
10
11# For original indices, calculate them
12n = len(items)
13for i, item in enumerate(reversed(items)):
14    original_index = n - 1 - i
15    print(f"{original_index}: {item}")
16# 3: d
17# 2: c
18# 1: b
19# 0: a

Reversing and Iterating a Dictionary

python
1data = {"a": 1, "b": 2, "c": 3}
2
3# Python 3.8+: reversed() works on dictionaries
4for key in reversed(data):
5    print(key, data[key])
6# c 3
7# b 2
8# a 1
9
10# Reverse items
11for key, value in reversed(data.items()):
12    print(f"{key} = {value}")

Dictionaries maintain insertion order in Python 3.7+, and reversed() on dicts was added in Python 3.8.

Modifying a List While Iterating Backwards

Backwards iteration is safe for removing elements because it does not shift unvisited indices:

python
1numbers = [1, -2, 3, -4, 5, -6]
2
3# Remove negative numbers — safe because we iterate backwards
4for i in range(len(numbers) - 1, -1, -1):
5    if numbers[i] < 0:
6        numbers.pop(i)
7
8print(numbers)  # [1, 3, 5]
9
10# Forward iteration causes bugs:
11# numbers = [1, -2, 3, -4, 5, -6]
12# for i in range(len(numbers)):
13#     if numbers[i] < 0:
14#         numbers.pop(i)  # Shifts indices — skips elements!

Performance Comparison

python
1import timeit
2
3data = list(range(1_000_000))
4
5# reversed() — O(1) memory, creates an iterator
6timeit.timeit(lambda: list(reversed(data)), number=10)
7
8# Slice [::-1] — O(n) memory, creates a copy
9timeit.timeit(lambda: data[::-1], number=10)
10
11# range with negative step — O(1) memory
12timeit.timeit(lambda: [data[i] for i in range(len(data)-1, -1, -1)], number=10)
MethodMemorySpeedCreates Copy
reversed()O(1)FastNo
[::-1]O(n)FastYes
range(n-1, -1, -1)O(1)ModerateNo

Common Pitfalls

  • Using reversed() on a generator: reversed() requires a sequence with a known length. Passing a generator or iterator raises TypeError: argument to reversed() must be a sequence. Convert to a list first: reversed(list(gen)).
  • Off-by-one in range() stop value: range(5, 0, -1) produces 5, 4, 3, 2, 1 — it does not include 0. To include 0, use range(5, -1, -1) which gives 5, 4, 3, 2, 1, 0.
  • Modifying a list while iterating forward: Removing elements during forward iteration shifts indices and skips elements. Iterate backwards with range(len(lst)-1, -1, -1) or use list comprehension [x for x in lst if condition] instead.
  • Slicing large sequences: data[::-1] creates a full copy of the list, doubling memory usage. For million-element lists, use reversed() which creates only an iterator.
  • Expecting reversed() to return a list: reversed() returns an iterator, not a list. Calling reversed(data)[0] raises TypeError. Wrap in list() if you need random access: list(reversed(data))[0].

Summary

  • reversed() is the most Pythonic way — creates a memory-efficient reverse iterator
  • range(n-1, -1, -1) is best when you need the index for backwards traversal
  • [::-1] creates a reversed copy — convenient but uses O(n) extra memory
  • Use backwards iteration when removing elements from a list to avoid index-shifting bugs
  • reversed() works on dicts in Python 3.8+
  • Prefer reversed() over [::-1] for large sequences to save memory

Course illustration
Course illustration

All Rights Reserved.