Python Tutorial

Convert Letters to Numbers in Python

AlphaCoder Team|May 24, 2026|11 min read

Python makes letter-to-number conversion exceptionally clean thanks to built-in functions like ord() and chr(). Whether you need a quick one-liner for a script, a robust encoder class for a project, or batch processing for data analysis, Python has the tools. This guide covers every approach from basic to advanced, with production-ready code examples.

The Basics: ord() and chr()

Python's ord() function returns the Unicode code point (identical to ASCII for English letters) of a single character. Since uppercase A has code point 65 and alphabet position 1, the conversion formula is:

position = ord(letter.upper()) - 64

# Examples
ord('A') - 64  # → 1
ord('Z') - 64  # → 26
ord('m') - 64  # → 45 (wrong! lowercase m is 109)
ord('M') - 64  # → 13 (correct)

# Always use .upper() for consistency
ord('m'.upper()) - 64  # → 13

The reverse operation uses chr():

letter = chr(position + 64)

chr(1 + 64)   # → 'A'
chr(26 + 64)  # → 'Z'
chr(13 + 64)  # → 'M'

# For lowercase
chr(position + 96)
chr(1 + 96)   # → 'a'

Converting a Full String

List Comprehension (One-Liner)

text = "HELLO WORLD"
numbers = [ord(c.upper()) - 64 for c in text if c.isalpha()]
# → [8, 5, 12, 12, 15, 23, 15, 18, 12, 4]

# With separator
encoded = "-".join(str(n) for n in numbers)
# → "8-5-12-12-15-23-15-18-12-4"

Preserving Word Boundaries

def encode_a1z26(text, sep="-", word_sep=" / "):
    """Encode text to A1Z26 with word boundaries."""
    words = text.split()
    encoded_words = []
    for word in words:
        nums = [str(ord(c.upper()) - 64)
                for c in word if c.isalpha()]
        encoded_words.append(sep.join(nums))
    return word_sep.join(encoded_words)

encode_a1z26("Hello World")
# → "8-5-12-12-15 / 23-15-18-12-4"

encode_a1z26("Python", sep=",")
# → "16,25,20,8,15,14"

Decoding Numbers Back to Letters

def decode_a1z26(encoded, sep="-", word_sep=" / "):
    """Decode A1Z26 number string back to text."""
    words = encoded.split(word_sep)
    decoded_words = []
    for word in words:
        letters = [chr(int(n.strip()) + 64)
                   for n in word.split(sep)
                   if n.strip().isdigit() and 1 <= int(n.strip()) <= 26]
        decoded_words.append("".join(letters))
    return " ".join(decoded_words)

decode_a1z26("8-5-12-12-15 / 23-15-18-12-4")
# → "HELLO WORLD"

decode_a1z26("16,25,20,8,15,14", sep=",")
# → "PYTHON"

Using the string Module

An alternative approach uses the string module, which provides ascii_uppercase and ascii_lowercase constants:

import string

# Create a mapping dictionary
letter_to_num = {letter: idx + 1
                 for idx, letter in enumerate(string.ascii_uppercase)}
# {'A': 1, 'B': 2, 'C': 3, ..., 'Z': 26}

# Convert using the dictionary
text = "CIPHER"
numbers = [letter_to_num[c] for c in text.upper() if c in letter_to_num]
# → [3, 9, 16, 8, 5, 18]

# Reverse mapping
num_to_letter = {v: k for k, v in letter_to_num.items()}
decoded = "".join(num_to_letter[n] for n in numbers)
# → "CIPHER"

Building a Complete A1Z26 Cipher Class

class A1Z26Cipher:
    """Complete A1Z26 encoder/decoder with configurable options."""

    def __init__(self, sep="-", word_sep=" / ",
                 case="upper", zero_indexed=False):
        self.sep = sep
        self.word_sep = word_sep
        self.case = case
        self.offset = 0 if zero_indexed else 1

    def encode_char(self, char):
        """Convert a single letter to its position number."""
        if not char.isalpha():
            return None
        return ord(char.upper()) - 65 + self.offset

    def decode_num(self, num):
        """Convert a position number to a letter."""
        code = num + 65 - self.offset
        if 65 <= code <= 90:
            letter = chr(code)
            return letter.lower() if self.case == "lower" else letter
        return None

    def encode(self, text):
        """Encode a full string to A1Z26."""
        words = text.split()
        encoded_words = []
        for word in words:
            nums = [str(self.encode_char(c))
                    for c in word if c.isalpha()]
            encoded_words.append(self.sep.join(nums))
        return self.word_sep.join(encoded_words)

    def decode(self, encoded):
        """Decode an A1Z26 string back to text."""
        words = encoded.split(self.word_sep)
        decoded_words = []
        for word in words:
            parts = word.split(self.sep)
            letters = []
            for p in parts:
                p = p.strip()
                if p.isdigit():
                    letter = self.decode_num(int(p))
                    if letter:
                        letters.append(letter)
            decoded_words.append("".join(letters))
        return " ".join(decoded_words)

    def word_value(self, text):
        """Calculate the sum of all letter positions."""
        return sum(self.encode_char(c) for c in text
                   if c.isalpha())

# Usage
cipher = A1Z26Cipher()
cipher.encode("Hello World")
# → "8-5-12-12-15 / 23-15-18-12-4"

cipher.decode("8-5-12-12-15 / 23-15-18-12-4")
# → "HELLO WORLD"

cipher.word_value("PYTHON")
# → 98 (16+25+20+8+15+14)

Batch Processing with Files

def process_file(input_path, output_path, mode="encode"):
    """Batch encode or decode a text file."""
    cipher = A1Z26Cipher()
    with open(input_path, "r") as f_in, \
         open(output_path, "w") as f_out:
        for line in f_in:
            if mode == "encode":
                f_out.write(cipher.encode(line.strip()) + "\n")
            else:
                f_out.write(cipher.decode(line.strip()) + "\n")

# Encode a file
process_file("message.txt", "encoded.txt", mode="encode")

# Decode a file
process_file("encoded.txt", "decoded.txt", mode="decode")

Performance Considerations

For most use cases, the simple ord(c) - 64 approach is fastest because it performs a single integer subtraction per character. The dictionary lookup method has slightly more overhead due to hash table lookups. For processing millions of characters:

# Fastest: direct arithmetic
[ord(c) - 64 for c in text.upper() if c.isalpha()]

# Slightly slower: dictionary lookup
letter_map = {chr(i + 64): i for i in range(1, 27)}
[letter_map[c] for c in text.upper() if c in letter_map]

# For very large texts, use bytes for speed
text_bytes = text.upper().encode('ascii', errors='ignore')
[b - 64 for b in text_bytes if 65 <= b <= 90]

No coding required: Convert text instantly with our free online A1Z26 Converter — runs entirely in your browser.

Frequently Asked Questions

What is the difference between ord() and the A1Z26 position?

ord() returns the Unicode/ASCII code point. For uppercase English letters, this ranges from 65 (A) to 90 (Z). The A1Z26 position is obtained by subtracting 64: ord('A') - 64 = 1. For lowercase letters (97-122), subtract 96 instead, or convert to uppercase first with .upper().

Does Python 3 handle Unicode differently for ord()?

In Python 3, strings are Unicode by default. ord() returns the Unicode code point, which is identical to ASCII for characters 0-127 (including all English letters). For A1Z26 purposes, there is no difference between Python 2 and Python 3 when working with English letters.

How do I handle accented characters like e-acute?

Accented characters (e.g., e with acute) have Unicode code points above 127 and do not have standard A1Z26 positions. You can normalize them using unicodedata.normalize('NFKD', text) to strip diacritics before converting, which turns e-acute into a plain "e."

Can I use numpy for vectorized conversion?

Yes. Convert the string to a numpy array of character codes, then subtract 64: import numpy as np; codes = np.frombuffer(text.upper().encode(), dtype=np.uint8); positions = codes[(codes >= 65) & (codes <= 90)] - 64. This is extremely fast for large texts.

Related Articles