To swap characters in a string in Python, turn the string into a list, swap the two positions with tuple unpacking, and join it back. Strings are immutable, so you always build a new string:
chars = list("Dnever")
chars[1], chars[2] = chars[2], chars[1]
"".join(chars) # 'Denver'
The outputs below come from Python 3.12.5 in a Windows Command Prompt, and the examples are small US place names, so you can check each result by eye.
You’ll see the list approach, a slicing function you can reuse, swapping the first and last letters, swapping adjacent pairs, and swapping every occurrence of two characters with translate().
Swap two characters in a string with a list and join()
A list is mutable, so it allows the swap that a string refuses. Here a typo has swapped the second and third letters of Denver:
city = "Dnever" # typo: the 2nd and 3rd letters are swapped
chars = list(city)
chars[1], chars[2] = chars[2], chars[1]
fixed = "".join(chars)
print(chars)
print(city, "->", fixed)
Output:
['D', 'e', 'n', 'v', 'e', 'r']
Dnever -> Denver

The line a, b = b, a is the same tuple trick you’d use to swap two numbers in Python. It also works on any list, as in swapping two elements in a list.
Why can’t I swap characters in a Python string directly?
Python strings can’t be changed in place. Assigning to an index raises a TypeError before anything is swapped:
city = "Dnever"
city[1], city[2] = city[2], city[1]
Output:
Traceback (most recent call last):
File "C:\pyguides\swap_error.py", line 2, in <module>
city[1], city[2] = city[2], city[1]
~~~~^^^
TypeError: 'str' object does not support item assignment

'str' object does not support item assignment.That’s why every method here returns a new string. If you want to remove a letter rather than move it, see removing a character from a string by index.
Swap characters at two positions with string slicing
If you’d rather skip the list, rebuild the string from slices around the two positions. I sort the indexes first, so the caller can pass them in either order:
def swap_chars(text, i, j):
"""Return a copy of text with the characters at i and j swapped."""
if i == j:
return text
if i > j:
i, j = j, i
return text[:i] + text[j] + text[i + 1:j] + text[i] + text[j + 1:]
print(swap_chars("Tusla", 2, 3)) # Tulsa
print(swap_chars("Tusla", 3, 2)) # order of i and j doesn't matter
print(swap_chars("Austin", 0, -1)) # negative index -> last character
print(swap_chars("Denver", 2, 2)) # same index: unchanged
Output:
Tulsa
Tulsa
AustiAnustin
Denver
The third line is garbage: with -1, the comparison sorts the indexes the wrong way round, so the slices overlap and Austin comes back as AustiAnustin. The list version handles negative indexes for free, but the slicing version needs them normalized first.
Converting the indexes with % len(text) fixes it. Modulo would also quietly wrap an index that’s too big, so I check the range first and raise an IndexError:
def swap_chars(text, i, j):
n = len(text)
if not (-n <= i < n and -n <= j < n):
raise IndexError(f"index out of range for a string of length {n}")
i, j = sorted((i % n, j % n)) # turn -1 into the last position first
if i == j:
return text
return text[:i] + text[j] + text[i + 1:j] + text[i] + text[j + 1:]
print(swap_chars("Austin", 0, -1))
print(swap_chars("Tampa", -2, 1))
try:
swap_chars("Miami", 0, 9)
except IndexError as err:
print("IndexError:", err)
Output:
nustiA
Tpmaa
IndexError: index out of range for a string of length 5

If slices like text[i + 1:j] look cryptic, my guide to string slicing in Python walks through start, stop and step.
Swap the first and last characters of a string
This one comes up in coding exercises a lot. Take the last character, the middle, then the first, and guard strings shorter than two characters:
def swap_first_last(text):
if len(text) < 2:
return text
return text[-1] + text[1:-1] + text[0]
for word in ["Dallas", "Reno", "NY", "A", ""]:
print(f"{word!r:10} -> {swap_first_last(word)!r}")
Output:
'Dallas' -> 'sallaD'
'Reno' -> 'oenR'
'NY' -> 'YN'
'A' -> 'A'
'' -> ''
Without the length check, a one-letter string would come back doubled. For more on picking out the ends, see getting the first character of a string.
Swap adjacent pairs of characters in a string
To swap every pair (positions 0 and 1, 2 and 3, and so on), step through the list two at a time. With an odd length, the last character has no partner and stays where it is:
def swap_pairs(text):
chars = list(text)
for i in range(0, len(chars) - 1, 2):
chars[i], chars[i + 1] = chars[i + 1], chars[i]
return "".join(chars)
print(swap_pairs("abcdef"))
print(swap_pairs("Chicago")) # odd length: last letter stays put
print(swap_pairs(swap_pairs("Chicago"))) # swapping twice gives the original back
# the same thing with slicing and zip
s = "abcdef"
print("".join(b + a for a, b in zip(s[::2], s[1::2])))
Output:
badcfe
hCcigao
Chicago
badcfe
Swapping pairs twice returns the original, which makes a handy test. If you need every character reversed instead, use the approaches in reversing a string in Python.
Swap every occurrence of two characters with translate()
Sometimes you don’t want positions at all: you want every A to become T and every T to become A. Chained replace() calls fail at that, because the second call undoes the first. str.maketrans() swaps both in one pass:
price = "1.299,99" # European style: dot for thousands, comma for decimals
print(price.translate(str.maketrans(",.", ".,")))
dna = "AATGCCTA"
print(dna.translate(str.maketrans("AT", "TA"))) # every A <-> T in one pass
# the classic replace() mistake: the second call undoes the first
print("tattoo".replace("t", "o").replace("o", "t"))
print("tattoo".translate(str.maketrans("to", "ot")))
Output:
1,299.99
TTAGCCAT
tatttt
oaoott

The comma-and-dot price fix has its own walkthrough in swapping commas and dots in a string. To change several unrelated characters at once, see replacing multiple characters in a string.
Swap uppercase and lowercase characters with swapcase()
If “swap characters” means flipping the case of every letter, that’s built in:
print("Hello, Phoenix!".swapcase())
print("pYTHON".swapcase())
print("Straße".swapcase(), "->", "Straße".swapcase().swapcase()) # not always reversible
Output:
hELLO, pHOENIX!
Python
sTRASSE -> Strasse
Watch out with characters like the German ß: its uppercase is two letters, so swapcase() twice doesn’t give the original back.
Which way to swap characters should you use?
| Goal | Use | Example |
|---|---|---|
| Swap two positions | list() + join() | 'Dnever' → 'Denver' |
| Reusable function, no list | Slicing with sorted indexes | swap_chars(s, 1, 4) |
| First and last | s[-1] + s[1:-1] + s[0] | 'Dallas' → 'sallaD' |
| Every pair | Loop with step 2 | 'abcdef' → 'badcfe' |
| Every a ↔ b | translate(maketrans()) | '1.299,99' → '1,299.99' |
| Upper ↔ lower | swapcase() | 'pYTHON' → 'Python' |
Want to keep practicing with strings? Try these next:
- Sort a string in Python
- Concatenate strings in Python
- Fix IndexError: string index out of range
- Split a string into an array in Python
Frequently asked questions
How do I swap two characters in a string in Python?
Convert it with list(s), swap with chars[i], chars[j] = chars[j], chars[i], then use "".join(chars).
Why do I get ‘str’ object does not support item assignment?
Strings are immutable. Build a new string with a list or slicing instead of assigning to s[i].
How do I swap the first and last character of a string?
Use s[-1] + s[1:-1] + s[0] for strings with at least two characters.
How do I swap two letters everywhere in a string?
Use s.translate(str.maketrans("ab", "ba")). Chained replace() calls undo each other.
Is there a built-in function to swap characters in Python?
Not by position. swapcase() only