Hey everyone,
It's been a little while since I've done a "Fun with Python" post, with the last one being the word_clock.py script that tells time with words. Today, I've got another fun little project to share: a command-line tool I wrote to convert integers to and from Roman numerals.
Roman numerals are an interesting programming puzzle. It's not just a simple one-to-one mapping of numbers to letters; you have to account for the subtractive principle where "IV" is 4 (not "IIII") and "CM" is 900 (not "DCCCC"). It makes for a great little logic challenge.
roman.py ScriptI put together a simple Python script to handle these conversions. It's a command-line tool that can take an integer and give you the Roman numeral, or take a Roman numeral and give you the integer.
A neat feature I included is support for larger numbers using the vinculum (or overbar) notation, where a bar over a numeral multiplies its value by 1,000. So, X̅ is 10,000, C̅ is 100,000, and so on.

1994, the script works its way down from the largest values. It sees that 1994 is bigger than 1000, so it adds an "M" and subtracts 1000, leaving 994. Then it sees 900, adds "CM" and subtracts 900, leaving 94. It continues this for 90 ("XC") and 4 ("IV") until it gets to zero, building the final string: MCMXCIV.Here is the full script. It's self-contained and doesn't require any external libraries.
#!/usr/bin/env python3
import argparse
from typing import List, Tuple
# Ordered list of Roman numeral symbols (value, symbol) in descending order.
# Includes overbar (vinculum) characters for larger numbers.
SYMBOLS: List[Tuple[int, str]] = [
(1000000, "M̅"),
(100000, "C̅"),
(10000, "X̅"),
(1000, "M"),
(900, "CM"),
(500, "D"),
(400, "CD"),
(100, "C"),
(90, "XC"),
(50, "L"),
(40, "XL"),
(10, "X"),
(9, "IX"),
(5, "V"),
(4, "IV"),
(1, "I"),
]
# Derived mapping for quick symbol → value lookups.
SYMBOL_TO_VALUE = {s: v for v, s in SYMBOLS}
def int_to_roman(num: int) -> str:
"""
Convert an integer to a Roman numeral.
"""
if not 0 < num < 4000000: # Practical upper bound
raise ValueError("Integer must be between 1 and 3,999,999.")
roman_numeral = ""
# Build the numeral greedily from largest to smallest value.
for value, symbol in SYMBOLS:
while num >= value:
roman_numeral += symbol
num -= value
return roman_numeral
def roman_to_int(roman: str) -> int:
"""
Convert a Roman numeral to an integer.
"""
if not roman:
raise ValueError("Roman numeral cannot be empty.")
roman = roman.upper()
# Sort symbols by length so multi-char symbols (like 'CM') match first.
symbols = sorted(SYMBOL_TO_VALUE.keys(), key=len, reverse=True)
value = 0
while roman:
matched = False
for symbol in symbols:
if roman.startswith(symbol):
value += SYMBOL_TO_VALUE[symbol]
roman = roman[len(symbol) :]
matched = True
break
if not matched:
raise ValueError(f"Invalid character or sequence in Roman numeral: '{roman}'")
return value
def main() -> int:
"""Entry-point for command-line interface."""
parser = argparse.ArgumentParser(
description="Convert between integers and Roman numerals."
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"-i", "--integer", type=int, help="Integer to convert to Roman numeral."
)
group.add_argument(
"-r", "--roman", type=str, help="Roman numeral to convert to integer."
)
args = parser.parse_args()
try:
if args.integer is not None:
print(int_to_roman(args.integer))
else:
print(roman_to_int(args.roman))
except ValueError as exc:
parser.error(str(exc))
return 0
if __name__ == "__main__":
main()
Using it from your terminal is straightforward:
To convert an integer to a Roman numeral:
python3 roman.py --integer 2025
Output: MMXXV
To convert a Roman numeral to an integer:
python3 roman.py --roman MCMXCIV
Output: 1994
And here's an example with a larger number:
python3 roman.py -i 12345
Output: X̅MMCCCXLV
It's a simple, fun project and a great way to practice some basic algorithm logic. Hope you enjoy it!
As always,
Michael Garcia a.k.a. TheCrazyGM
While I never learned Roman numerals in depth, I had no idea that they presented such interesting challenges to converting to and from Arabic numerals. Very cool, I learned something! 😁 🙏 💚 ✨ 🤙
All the vibe coders should also learn to code with great content like this! We can make a collextion later.
!PAKX
!PIMP
!PIZZA
View or trade
PAKXtokens.Use !PAKX command if you hold enough balance to call for a @pakx vote on worthy posts! More details available on PAKX Blog.
$PIZZA slices delivered:
@ecoinstant(2/20) tipped @thecrazygm
Come get MOONed!