To reverse a string in Python, slice it with a step of -1:
text = "Python Programming"
text[::-1] # 'gnimmargorP nohtyP'
There is no reverse() method on strings, because strings cannot be changed in place. Every approach below builds a new string.
All timings and output come from real runs on Python 3.12.5.
Reversing a string with slicing
The slice takes three numbers, start:stop:step. Leave the first two out and set the step to -1:
text = "Python Programming"
print(text[::-1])
# the three numbers are start:stop:step, and a step of -1 walks backwards
print("step -1 :", text[::-1])
print("step -2 :", text[::-2], " every second character, backwards")
Output:
gnimmargorP nohtyP
step -1 : gnimmargorP nohtyP
step -2 : gimroPnhy every second character, backwards
A negative step walks the string backwards. -2 takes every second character on the way, which is occasionally useful and always confusing to read.
This is the version to use. It is the shortest, the fastest, and the one every Python developer recognises instantly.
Three ways to reverse a string in Python
Slicing is not the only option, though it is the best one:
text = "Python"
# 1. slicing, the idiomatic answer
print("slice :", text[::-1])
# 2. reversed() returns an iterator, so join it back into a string
print("reversed() :", "".join(reversed(text)))
# 3. a list, reversed in place
chars = list(text)
chars.reverse()
print("list.reverse :", "".join(chars))
Output:
slice : nohtyP
reversed() : nohtyP
list.reverse : nohtyP

| Method | Returns | Notes |
|---|---|---|
text[::-1] | A string | Fastest and shortest |
"".join(reversed(text)) | A string | Clearer intent, slower |
list(text) then .reverse() | A list | Needs joining back |
A for loop | A string | For coursework, not production |
reversed() returns an iterator rather than a string, so printing it directly gives you something like <reversed object>. It has to be joined.
Why str object has no attribute reverse
This is the most common error people hit here, and the reason is worth understanding:
text = "Python"
# strings are immutable, so there is nothing to reverse in place
try:
text.reverse()
except AttributeError as err:
print("text.reverse() ->", type(err).__name__ + ":", err)
print()
print("list has a reverse method:", hasattr(list, "reverse"))
print("str has a reverse method:", hasattr(str, "reverse"))
print()
print("every string operation returns a NEW string, so you must assign the result:")
text = text[::-1]
print("text is now:", text)
Output:
text.reverse() -> AttributeError: 'str' object has no attribute 'reverse'
list has a reverse method: True
str has a reverse method: False
every string operation returns a NEW string, so you must assign the result:
text is now: nohtyP

Strings are immutable. Once created, the characters cannot be rearranged, so there is nothing for a reverse() method to do.
Lists are mutable, which is why list.reverse() exists and returns None: it changes the list rather than producing a new one.
The practical consequence is that you must assign the result. text[::-1] on its own does nothing to text. That’s true of every string method, including the ones that remove newlines.
Reverse a string using a for loop in Python
Assignments often ask for this specifically. The trick is to put each new character at the front:
text = "Python"
# build the result by putting each new character at the FRONT
reversed_text = ""
for char in text:
reversed_text = char + reversed_text
print("for loop :", reversed_text)
print()
# the same idea counting backwards through the indexes
out = ""
for i in range(len(text) - 1, -1, -1):
out += text[i]
print("by index :", out)
print()
# and with while
i, out = len(text) - 1, ""
while i >= 0:
out += text[i]
i -= 1
print("while :", out)
Output:
for loop : nohtyP
by index : nohtyP
while : nohtyP

reversed_text = char + reversed_text is the key line. Each character goes before everything collected so far, so the order comes out backwards.
The index version, range(len(text) - 1, -1, -1), counts down to zero. The second -1 is the stop value, which is excluded, so index 0 is still included.
Reversing a string with recursion
A recursive version reads neatly and is a common interview question:
def reverse(text):
if len(text) <= 1: # a single character is its own reverse
return text
return reverse(text[1:]) + text[0]
print(reverse("Python"))
print(reverse("a"))
print(reverse(""))
print()
import sys
print("recursion limit:", sys.getrecursionlimit())
print("so this breaks on strings longer than about 1000 characters")
Output:
nohtyP
a
recursion limit: 1000
so this breaks on strings longer than about 1000 characters
The base case is a string of one character or less, which is already its own reverse. Everything else is the reverse of the tail, plus the first character.
It is also the worst option in practice. Each call slices a new string, and the recursion limit caps you at around a thousand characters. See recursion in Python for why that limit exists.
Which string reversal method is fastest in Python?
On a 2,000-character string the gap is not subtle:
import timeit
text = "x" * 2000
def with_loop(s):
out = ""
for ch in s:
out = ch + out
return out
tests = {
"text[::-1]": lambda: text[::-1],
'"".join(reversed(text))': lambda: "".join(reversed(text)),
"manual for loop": lambda: with_loop(text),
}
for name, fn in tests.items():
elapsed = timeit.timeit(fn, number=2000)
print(f"{name:<24} {elapsed * 1000:>8.1f} ms")
print()
print("the slice wins because it runs entirely in C and allocates once")
Output:
text[::-1] 1.7 ms
"".join(reversed(text)) 27.2 ms
manual for loop 282.4 ms
the slice wins because it runs entirely in C and allocates once

The loop is slow because strings are immutable. out = ch + out builds a brand new string on every single iteration, copying everything collected so far.
That makes the loop quadratic: doubling the string length roughly quadruples the work. The slice allocates once and copies once.
Use the loop only when an exercise demands it. For real code, the slice is the answer every time.
Reversing the words in a Python string
“Reverse a string” sometimes means the word order rather than the letters:
sentence = "Python is a great language"
print("characters reversed:", sentence[::-1])
print("words reversed :", " ".join(sentence.split()[::-1]))
print()
# each word reversed but the order kept
print("each word reversed :", " ".join(word[::-1] for word in sentence.split()))
Output:
characters reversed: egaugnal taerg a si nohtyP
words reversed : language great a is Python
each word reversed : nohtyP si a taerg egaugnal
split() breaks the sentence into words, the slice reverses that list, and join puts it back together.
The third line reverses each word in place while keeping the sentence order, which is a different effect again. Be sure which one the task wants.
Checking for a palindrome in Python
Reversal’s most common use is testing whether text reads the same both ways:
def is_palindrome(text):
cleaned = "".join(ch.lower() for ch in text if ch.isalnum())
return cleaned == cleaned[::-1]
for phrase in ["racecar", "A man a plan a canal Panama", "Python", "Was it a car or a cat I saw?"]:
print(f"{phrase!r:<32} {is_palindrome(phrase)}")
Output:
'racecar' True
'A man a plan a canal Panama' True
'Python' False
'Was it a car or a cat I saw?' True
The comprehension strips punctuation and spaces and lowercases everything first, so “A man a plan a canal Panama” is recognised.
isalnum() keeps only letters and digits. There is more on that check in the isdigit guide.
Reversing strings with accents or emoji
Slicing reverses code points, and some visible characters are made of several:
import unicodedata
# 'cafe' with a combining acute accent on the e
cafe = "café"
print("length :", len(cafe), "code points")
print("reversed :", ascii(cafe[::-1]))
print(" the accent is now attached to the wrong letter")
print()
fixed = unicodedata.normalize("NFC", cafe)
print("after NFC :", len(fixed), "code points")
print("reversed :", ascii(fixed[::-1]), " correct")
Output:
length : 5 code points
reversed : '\u0301efac'
the accent is now attached to the wrong letter
after NFC : 4 code points
reversed : '\xe9fac' correct

Normalising with NFC first combines letter-and-accent pairs into single characters, so they survive the reversal intact.
Emoji built from several joined code points need more than normalising, but for ordinary accented text NFC is enough.
Common string reversal mistakes
| Symptom | Cause | Fix |
|---|---|---|
AttributeError: no attribute 'reverse' | Strings are immutable | Use text[::-1] |
| Nothing changed | Result not assigned | text = text[::-1] |
Printed <reversed object> | reversed() gives an iterator | Wrap it in "".join() |
None returned | list.reverse() works in place | Reverse first, then join |
| Very slow on long text | Building with + in a loop | Use the slice |
| Accent on the wrong letter | Combining characters reversed | Normalise with NFC |
More Python string and sequence guides:
- Get the first n characters of a string
- Remove newlines from a string
- Reverse a list in Python
- Reverse a tuple
- Compare strings in Python
- Recursion in Python
Frequently asked questions
How do you reverse a string in Python?
Slice it with a step of -1: text[::-1]. Slicing is described in the Python sequence operations reference.
Why does str object has no attribute reverse?
Strings are immutable, so there is no in-place reverse() method. Lists have one because they can be changed.
How do I reverse a string using a for loop?
Start with an empty string and write result = char + result inside the loop, so each character is added at the front.
What is the fastest way to reverse a string in Python?
text[::-1]. On a 2,000-character string it was around a hundred times faster than a manual loop.
Why does reversed() print something odd?
It returns an iterator, not a string. Wrap it: "".join(reversed(text)).
How do I reverse the words in a sentence?
" ".join(sentence.split()[::-1]) reverses the word order while keeping each word intact.
How do I check if a string is a palindrome?
Strip out non-alphanumeric characters, lowercase it, then compare it with its reverse.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn