The ord() function in Python takes a single character and returns its Unicode code point, an integer. chr() does the reverse, so the two always round-trip:
ord("A") # 65
chr(65) # 'A'
ord("€") # 8364
I ran each example on Python 3.12.5 in a Windows Command Prompt, including the accented characters and symbols, so the numbers you see are exactly what Python returned.
You’ll see the codes for common characters, non-English text, the TypeError that trips people up, and practical uses: letter positions, a Caesar cipher and why sorting puts capitals first.
How ord() and chr() work
Every character has a number. For plain English letters, digits and punctuation, that number is the same as the old ASCII code:
for ch in ["A", "a", "Z", "0", " ", "$"]:
print(f"ord({ch!r}) = {ord(ch)}")
print(chr(65), chr(97), chr(36)) # chr() goes the other way
print(chr(ord("A") + 1)) # the next letter
Output:
ord('A') = 65
ord('a') = 97
ord('Z') = 90
ord('0') = 48
ord(' ') = 32
ord('$') = 36
A a $
B

chr() turns the numbers back into characters.chr(ord("A") + 1) is a neat way to get the next letter. The chr() function in Python covers the reverse direction in more depth.
ord() with accented letters and symbols
ord() isn’t limited to ASCII. It returns the full Unicode code point, which you’ll usually see written as U+ followed by hex digits:
for ch in ["é", "ñ", "€", "©", "°"]:
code = ord(ch)
print(f"{ch} ord = {code:>5} U+{code:04X} UTF-8 bytes: {len(ch.encode('utf-8'))}")
Output:
é ord = 233 U+00E9 UTF-8 bytes: 2
ñ ord = 241 U+00F1 UTF-8 bytes: 2
€ ord = 8364 U+20AC UTF-8 bytes: 3
© ord = 169 U+00A9 UTF-8 bytes: 2
° ord = 176 U+00B0 UTF-8 bytes: 2

The code point and the number of bytes are different things: the code point identifies the character, and the encoding decides how it’s stored. To check whether text is plain ASCII, see checking if a string is ASCII.
TypeError: ord() expected a character, but string of length found
ord() accepts exactly one character. Pass a longer string, or an empty one, and you get a TypeError that tells you the length it received:
for text in ["A", "abc", ""]:
try:
print(repr(text), "->", ord(text))
except TypeError as err:
print(repr(text), "-> TypeError:", err)
print([ord(ch) for ch in "abc"]) # one code per character
Output:
'A' -> 65
'abc' -> TypeError: ord() expected a character, but string of length 3 found
'' -> TypeError: ord() expected a character, but string of length 0 found
[97, 98, 99]

To get codes for a whole string, loop over it: [ord(ch) for ch in text]. Converting a string to ASCII has more ways to do that.
Using ord() to get a letter’s position in the alphabet
Because letters have consecutive codes, subtracting ord("a") gives a letter’s position. Lowercase first so capitals work too:
def letter_position(ch):
"""A/a -> 1, B/b -> 2, ... Z/z -> 26."""
return ord(ch.lower()) - ord("a") + 1
print([letter_position(c) for c in "Texas"])
def is_upper_ascii(ch):
return ord("A") <= ord(ch) <= ord("Z")
print(is_upper_ascii("T"), is_upper_ascii("t"), "T".isupper())
Output:
[20, 5, 24, 1, 19]
True False True
For checks like “is it uppercase”, the string methods such as isupper() are clearer and also handle non-English letters. Converting letters to numbers covers other approaches.
A Caesar cipher with ord() and chr()
Shifting letters is the classic ord() exercise. Convert to a number, shift, wrap around with % 26, and convert back:
def shift(text, n):
out = []
for ch in text:
if "a" <= ch <= "z":
out.append(chr((ord(ch) - ord("a") + n) % 26 + ord("a")))
elif "A" <= ch <= "Z":
out.append(chr((ord(ch) - ord("A") + n) % 26 + ord("A")))
else:
out.append(ch) # leave spaces and punctuation alone
return "".join(out)
secret = shift("Meet in Austin at noon", 3)
print(secret)
print(shift(secret, -3))
Output:
Phhw lq Dxvwlq dw qrrq
Meet in Austin at noon
It’s a toy, not real encryption, but it’s a good way to see why character codes are useful. The same shift-and-wrap idea shows up in checksums and simple encoding schemes.
Why do capital letters sort before lowercase in Python?
Python compares strings by their code points, character by character. Every capital letter has a smaller code than every lowercase one, so plain sorting puts capitals first:
cities = ["denver", "Austin", "boston", "Chicago"]
print(sorted(cities)) # capitals first: 'C' (67) < 'b' (98)
print(sorted(cities, key=str.lower)) # alphabetical, ignoring case
print("'Z' < 'a':", "Z" < "a", "|", ord("Z"), "<", ord("a"))
print(ord("5"), "is not", int("5")) # a digit's code isn't its value
Output:
['Austin', 'Chicago', 'boston', 'denver']
['Austin', 'boston', 'Chicago', 'denver']
'Z' < 'a': True | 90 < 97
53 is not 5

key=str.lower sorts the way people expect.The last line is a common slip with digits: ord("5") is 53, not 5. Use int("5") when you want a digit’s value.
ord() in Python: quick reference
| Goal | Code | Result |
|---|---|---|
| Code of a character | ord("A") | 65 |
| Character from a code | chr(65) | ‘A’ |
| Next letter | chr(ord("A") + 1) | ‘B’ |
| Position in the alphabet | ord(c.lower()) - ord("a") + 1 | 1 to 26 |
| Codes for a whole string | [ord(c) for c in text] | a list of ints |
| Unicode notation | f"U+{ord(c):04X}" | ‘U+20AC’ for € |
| Case-insensitive sort | sorted(words, key=str.lower) | alphabetical |
More guides on characters and text in Python:
- The chr() function in Python
- The ascii() function in Python
- Convert a string to ASCII
- Check if a string is ASCII
- The len() function in Python
Frequently asked questions
What does ord() do in Python?
It returns the Unicode code point of a single character as an integer. ord("A") is 65.
What is the opposite of ord() in Python?
chr(). It takes a code point and returns the character, so chr(ord(c)) == c for any character.
Is ord() the same as ASCII?
For the first 128 characters, yes: Unicode code points match ASCII codes there. Beyond that, ord() returns Unicode values like 233 for é.
Why does ord() say ‘expected a character, but string of length 3 found’?
You passed a string with more than one character. Use [ord(c) for c in text] to convert each one.
How do I get the position of a letter in the alphabet?
Use ord(c.lower()) - ord("a") + 1, which gives 1 for a and 26 for z.
Can ord() handle emoji?
Yes, if the emoji is a single code point. Some emoji are built from several code points, which makes them strings of length greater than one. The ord() documentation describes the exact rule.

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