Editor

Unit Converter in Python

Beginner · Useful tools · about 40 minutes to build · 121 lines

Convert lengths, weights and temperatures between metric and imperial units from a short menu.

The program

Press Run and answer its questions in the box that appears under the code.

unit_converter.py
"""A unit converter for length, weight and temperature."""
import math
# How many metres make one of each length unit
LENGTH_UNITS = {
"mm": 0.001, "cm": 0.01, "m": 1.0, "km": 1000.0,
"in": 0.0254, "ft": 0.3048, "yd": 0.9144, "mi": 1609.344,
}
# How many kilograms make one of each weight unit (t = tonne, st = stone)
WEIGHT_UNITS = {
"mg": 0.000001, "g": 0.001, "kg": 1.0, "t": 1000.0,
"oz": 0.028349523125, "lb": 0.45359237, "st": 6.35029318,
}
# Celsius, Fahrenheit and Kelvin need formulas, not factors
TEMPERATURE_UNITS = ["C", "F", "K"]
ABSOLUTE_ZERO_C = -273.15
CATEGORIES = {
"1": ("Length", LENGTH_UNITS),
"2": ("Weight", WEIGHT_UNITS),
"3": ("Temperature", TEMPERATURE_UNITS),
}
QUIT_CHOICE = "4"
# Reading input
def choose_unit(prompt, units):
"""Ask until the user types one of the units (any letter case)."""
while True:
text = input(prompt).strip()
for unit in units:
if unit.lower() == text.lower():
return unit
print(f" Unknown unit. Choose from: {', '.join(units)}")
def read_number(prompt):
"""Ask until the user types an ordinary number (not nan or inf)."""
while True:
try:
value = float(input(prompt))
if math.isfinite(value):
return value
except ValueError:
pass
print(" Please type a number, like 5 or 98.6.")
# Converting
def convert_temperature(value, from_unit, to_unit):
"""Convert through Celsius; refuse anything colder than absolute zero."""
if from_unit == "F":
celsius = (value - 32) * 5 / 9
elif from_unit == "K":
celsius = value + ABSOLUTE_ZERO_C
else:
celsius = value
if round(celsius, 6) < ABSOLUTE_ZERO_C:
raise ValueError("That is colder than absolute zero (-273.15 C).")
if to_unit == "F":
return celsius * 9 / 5 + 32
if to_unit == "K":
return celsius - ABSOLUTE_ZERO_C
return celsius
def convert(value, from_unit, to_unit, units):
"""Convert a value between two units of the same kind."""
if units == TEMPERATURE_UNITS:
return convert_temperature(value, from_unit, to_unit)
if value < 0:
raise ValueError("Lengths and weights cannot be negative.")
# Go to the base unit (metres or kilograms), then to the target unit
return value * units[from_unit] / units[to_unit]
def format_value(value):
"""Round to 4 decimal places and drop zeros at the end: 3.1069, 1,000."""
if value != 0 and not 0.0001 <= abs(value) < 1e12:
return f"{value:.4g}" # very small or very large: 6.214e-07
text = f"{value:,.4f}".rstrip("0").rstrip(".")
return "0" if text == "-0" else text
def run_conversion(name, units):
"""Ask for two units and a value, then print the conversion."""
print(f"{name} units: {', '.join(units)}")
from_unit = choose_unit("From unit: ", units)
to_unit = choose_unit("To unit: ", units)
while True:
value = read_number(f"Value in {from_unit}: ")
try:
result = convert(value, from_unit, to_unit, units)
break
except ValueError as error:
print(f" {error}")
print(f" {format_value(value)} {from_unit} = {format_value(result)} {to_unit}")
def main():
"""Show the menu until the user quits."""
print("Unit converter")
while True:
print()
print("1) Length 2) Weight 3) Temperature 4) Quit")
choice = input("Choose 1-4: ").strip()
if choice in CATEGORIES:
name, units = CATEGORIES[choice]
run_conversion(name, units)
elif choice == QUIT_CHOICE:
print("Goodbye.")
break
else:
print(" Please choose a number from 1 to 4.")
if __name__ == "__main__":
main()

Open it in the full editor to save it, share it and keep building.

Sample run

What the program printed, and what was typed (in yellow).

Unit converter

1) Length  2) Weight  3) Temperature  4) Quit
Choose 1-4: 1
Length units: mm, cm, m, km, in, ft, yd, mi
From unit: km
To unit: mi
Value in km: 5
  5 km = 3.1069 mi

1) Length  2) Weight  3) Temperature  4) Quit
Choose 1-4: 2
Weight units: mg, g, kg, t, oz, lb, st
From unit: pounds
  Unknown unit. Choose from: mg, g, kg, t, oz, lb, st
From unit: lb
To unit: kg
Value in lb: 150
  150 lb = 68.0389 kg

1) Length  2) Weight  3) Temperature  4) Quit
Choose 1-4: 3
Temperature units: C, F, K
From unit: f
To unit: c
Value in F: 98.6
  98.6 F = 37 C

1) Length  2) Weight  3) Temperature  4) Quit
Choose 1-4: 3
Temperature units: C, F, K
From unit: c
To unit: f
Value in C: -300
  That is colder than absolute zero (-273.15 C).
Value in C: -40
  -40 C = -40 F

1) Length  2) Weight  3) Temperature  4) Quit
Choose 1-4: 4
Goodbye.

How it is built

1. Conversion factors

Each unit maps to how many metres or kilograms it holds. Converting is then two steps: multiply into the base unit, then divide by the target unit's factor.

# How many metres make one of each length unit
LENGTH_UNITS = {
    "mm": 0.001, "cm": 0.01, "m": 1.0, "km": 1000.0,
    "in": 0.0254, "ft": 0.3048, "yd": 0.9144, "mi": 1609.344,
}
# How many kilograms make one of each weight unit (t = tonne, st = stone)
WEIGHT_UNITS = {
    "mg": 0.000001, "g": 0.001, "kg": 1.0, "t": 1000.0,
    "oz": 0.028349523125, "lb": 0.45359237, "st": 6.35029318,
}
# Celsius, Fahrenheit and Kelvin need formulas, not factors
TEMPERATURE_UNITS = ["C", "F", "K"]
ABSOLUTE_ZERO_C = -273.15

2. Matching a unit in any case

Comparing unit.lower() with text.lower() accepts KM, km and Km. The loop repeats until the text matches one of the units, and shows the list when it does not.

def choose_unit(prompt, units):
    """Ask until the user types one of the units (any letter case)."""
    while True:
        text = input(prompt).strip()
        for unit in units:
            if unit.lower() == text.lower():
                return unit
        print(f"  Unknown unit. Choose from: {', '.join(units)}")

3. Temperatures need formulas

0 C is 32 F, so a single factor cannot work. The function converts to Celsius first, refuses anything colder than absolute zero with raise ValueError, then converts to the target.

def convert_temperature(value, from_unit, to_unit):
    """Convert through Celsius; refuse anything colder than absolute zero."""
    if from_unit == "F":
        celsius = (value - 32) * 5 / 9
    elif from_unit == "K":
        celsius = value + ABSOLUTE_ZERO_C
    else:
        celsius = value
    if round(celsius, 6) < ABSOLUTE_ZERO_C:
        raise ValueError("That is colder than absolute zero (-273.15 C).")
    if to_unit == "F":
        return celsius * 9 / 5 + 32
    if to_unit == "K":
        return celsius - ABSOLUTE_ZERO_C
    return celsius

4. One convert function for every category

Temperatures go to their own function and lengths and weights use the factor table. A negative length or weight raises ValueError, which run_conversion shows as a message before asking again.

def convert(value, from_unit, to_unit, units):
    """Convert a value between two units of the same kind."""
    if units == TEMPERATURE_UNITS:
        return convert_temperature(value, from_unit, to_unit)
    if value < 0:
        raise ValueError("Lengths and weights cannot be negative.")
    # Go to the base unit (metres or kilograms), then to the target unit
    return value * units[from_unit] / units[to_unit]

5. Readable numbers

Four decimal places with the zeros at the end removed turns 37.0000 into 37. Very small or very large values switch to the 6.214e-07 style.

def format_value(value):
    """Round to 4 decimal places and drop zeros at the end: 3.1069, 1,000."""
    if value != 0 and not 0.0001 <= abs(value) < 1e12:
        return f"{value:.4g}"  # very small or very large: 6.214e-07
    text = f"{value:,.4f}".rstrip("0").rstrip(".")
    return "0" if text == "-0" else text

Make it bigger

  • Add a volume category with litres, millilitres, cups and gallons.
  • Print a table converting 1 to 10 of the chosen unit.
  • Accept a whole request on one line, such as 5 km to mi.
  • Add speed, with km/h, mph and metres per second.

What it practises