LambdaLabTM
Computer Science · Class 11 · Strings Revisited
StringsASCII⏱️ 13 min read

Characters Are Numbers

A computer's memory holds nothing but numbers, so a letter has to be one too. Every character you can type has a code number agreed on by everybody — A is 65, a is 97, the space is 32 — and Python gives you both directions: ord() to see the number, chr() to get the character back.

1Why a letter needs a number

Nothing in a computer stores the shape of a letter. What is stored is a number, and the agreement about which number means which character is called an encoding. The oldest one still in everyday use is ASCII — the American Standard Code for Information Interchange — which gives codes 0 to 127 to the English letters, the digits, the punctuation and a handful of control characters.

Key Takeaway
ord() and chr() are one arrow pointing both ways. ord('A') is 65 — the ordinal, the character's place in the code. chr(65) is 'A' — the character that place holds. Each undoes the other: chr(ord('A')) is 'A'.
codes.py
# the code number behind every character of a word

word = 'Py3!'

for ch in word:
    print(ch, 'has the code', ord(ch))
Output
P has the code 80
y has the code 121
3 has the code 51
! has the code 33
back_again.py
# a code number turned back into a character

for code in range(65, 71):
    print(code, 'is', chr(code))
Output
65 is A
66 is B
67 is C
68 is D
69 is E
70 is F
Watch Out
ord() takes exactly one character. ord('AB') raises TypeError: ord() expected a character, but string of length 2 found, and ord('') complains in the same way. That is why ord() nearly always appears inside a loop, with the loop variable holding one character.

2The codes, drawn to scale

The numbers are not worth memorising. The shape they make is worth seeing once, because three facts fall straight out of it — drag the dial and watch for them.

🔡 Every character is a number

Drag the dial, or press a character. ord() goes one way, chr() comes back.

the character
A
a capital letter
ord('A') →← chr(65)
its code
65
65 + 32 = 97, which is 'a'
digits 0-9 · 4857capitals A-Z · 6590small a-z · 97122

The three coloured runs are unbroken and in order, which is what makes code >= 65 and code <= 90 a fair test for “is it a capital?”. Every letter of the alphabet is inside its run, and nothing else is.

The capitals come before the small letters, and the two runs are exactly 32 apart — 65 against 97, 90 against 122. That gap is why adding 32 changes the case, and why 'Z' < 'a' is True.

The line stops at 126 because ASCII does. The codes carry on: ord('₹') is 8377 and ord('अ') is 2309. Python strings are Unicode, so ord() and chr() work on those too — ASCII is just the first small piece of it.

3The three facts that matter

1
Each run is unbroken and in order

A to Z is 65 to 90 with nothing else in between, and a to z is 97 to 122. That is what makes code >= 65 and code <= 90 a fair test for 'is it a capital?' — every capital is inside, and nothing else is.

2
Capitals come before small letters

65 is less than 97, so 'Z' < 'a' is True. Comparing strings compares their codes, which is why a plain sort puts every capitalised word before every lower-case one.

3
The two alphabets are exactly 32 apart

65 against 97, 90 against 122. Add 32 to a capital's code and you get its small letter; take 32 away and you get the capital. That single fact is the whole of the next page.

the_gap.py
# the gap between the two alphabets, measured rather than remembered

print(ord('A'), ord('a'), ord('a') - ord('A'))
print(ord('Z'), ord('z'), ord('z') - ord('Z'))
print('Z' < 'a')
Output
65 97 32
90 122 32
True

4The digits are characters too

'7' is a character with the code 55, and it is not the number 7. That is the same distinction as the missing int() around input(), seen from underneath — and the codes explain the conversion that int() does:

digit_value.py
# a digit character, and the number it stands for

ch = '7'

print(ord(ch))
print(ord(ch) - ord('0'))
print(int(ch))
Output
55
7
7

ord(ch) - ord('0') is 55 − 48 = 7, which is what int(ch) gives you for a single digit. The digits run 48 to 57 in order, so subtracting the code of '0' turns any digit character into its value.

5ASCII is only the beginning: Unicode

ASCII has 128 codes, which is enough for English and nothing else. The modern agreement is Unicode, which gives a code to essentially every character in every writing system — Devanagari, Tamil, Chinese, the rupee sign, emoji. Python 3 strings are Unicode, so ord() and chr() work exactly the same way out there:

unicode.py
# ord() and chr() are not limited to ASCII

print(ord('A'), ord('₹'), ord('अ'))
print(chr(65), chr(8377), chr(2309))
Output
65 8377 2309
A ₹ अ
Key Takeaway
The first 128 Unicode codes are ASCII, unchanged. That is deliberate: ord('A') is 65 in both, so everything written for ASCII kept working when Unicode arrived. Think of ASCII as the first small shelf of a very large library — which is also why exam questions say “ASCII value” and Python simply says ord().
explore.py

6Recap

ord() takes a character, gives a number

Exactly one character. ord('AB') is a TypeError, which is why it usually appears inside a loop.

chr() takes a number, gives a character

The two undo each other: chr(ord(ch)) is ch, for any character at all.

48–57, 65–90, 97–122

Digits, capitals, small letters. Each run is unbroken, which is what makes a range check a fair test.

The alphabets are 32 apart

Add 32 to lower a capital, take 32 to raise a small letter. Measured with ord(), not remembered.

Quick Check

What is chr(ord('K'))?

Quick Check

Why is 'Z' < 'a' True in Python?

Quick Check

ord('7') is 55. What does ord('7') - ord('0') give?