Editor

Calculator Program in Python

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

Pick an operation from a menu, type two numbers and get the answer, with every result kept in a history you can look back at.

The program

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

calculator.py
"""A menu-driven calculator that keeps a history of every result."""
import math
# Menu number -> (name shown in the menu, symbol shown in the history)
OPERATIONS = {
"1": ("Add", "+"),
"2": ("Subtract", "-"),
"3": ("Multiply", "*"),
"4": ("Divide", "/"),
"5": ("Power", "^"),
"6": ("Remainder", "%"),
}
HISTORY_CHOICE = "7"
QUIT_CHOICE = "8"
MENU_COLUMNS = 4
# The menu and reading numbers
def show_menu():
"""Print every choice in neat rows of MENU_COLUMNS."""
items = [f"{key}) {name}" for key, (name, _) in OPERATIONS.items()]
items += [f"{HISTORY_CHOICE}) History", f"{QUIT_CHOICE}) Quit"]
for start in range(0, len(items), MENU_COLUMNS):
row = items[start:start + MENU_COLUMNS]
print("".join(f"{item:<14}" for item in row).rstrip())
def read_number(prompt):
"""Ask until the user types an ordinary number."""
while True:
text = input(prompt).strip()
try:
value = float(text)
except ValueError:
print(" That is not a number. Try something like 12 or 3.5.")
continue
if math.isfinite(value):
return value
print(" Please type an ordinary number.")
# Doing the sums
def calculate(a, b, symbol):
"""Return the result of a <symbol> b."""
if symbol == "+":
return a + b
if symbol == "-":
return a - b
if symbol == "*":
return a * b
if symbol == "/":
return a / b
if symbol == "%":
return a % b
return a ** b
def format_number(value):
"""Show 42.0 as 42 and hide tiny float errors like 0.30000000000000004."""
if value.is_integer() and abs(value) < 1e15:
return str(int(value))
return f"{value:.10g}"
def run_operation(choice, history):
"""Ask for two numbers, print the answer and add it to the history."""
name, symbol = OPERATIONS[choice]
a = read_number("First number: ")
b = read_number("Second number: ")
try:
result = calculate(a, b, symbol)
except ZeroDivisionError:
print(" You cannot divide by zero.")
return
except OverflowError:
print(" That answer is too large to show.")
return
# A negative number to a fractional power gives a complex number
if isinstance(result, complex):
print(" That power has no real-number answer.")
return
if not math.isfinite(result):
print(" That answer is too large to show.")
return
line = f"{format_number(a)} {symbol} {format_number(b)} = {format_number(result)}"
history.append(line)
print(f" {name}: {line}")
# History and the main loop
def show_history(history):
"""Print every calculation made so far, oldest first."""
if not history:
print(" No calculations yet.")
return
print(" History:")
for number, line in enumerate(history, start=1):
print(f" {number:>3}. {line}")
def main():
"""Show the menu and run the user's choices until they quit."""
history = []
print("Calculator")
while True:
print()
show_menu()
choice = input(f"Choose 1-{QUIT_CHOICE}: ").strip()
if choice in OPERATIONS:
run_operation(choice, history)
elif choice == HISTORY_CHOICE:
show_history(history)
elif choice == QUIT_CHOICE:
print(f"Goodbye. You made {len(history)} calculation(s).")
break
else:
print(" Please choose a number from the menu.")
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).

Calculator

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 1
First number: 12
Second number: 30
  Add: 12 + 30 = 42

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 4
First number: 7
Second number: 0
  You cannot divide by zero.

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 3
First number: abc
  That is not a number. Try something like 12 or 3.5.
First number: 2.5
Second number: 4
  Multiply: 2.5 * 4 = 10

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 5
First number: 2
Second number: 10
  Power: 2 ^ 10 = 1024

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 0
  Please choose a number from the menu.

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 7
  History:
    1. 12 + 30 = 42
    2. 2.5 * 4 = 10
    3. 2 ^ 10 = 1024

1) Add        2) Subtract   3) Multiply   4) Divide
5) Power      6) Remainder  7) History    8) Quit
Choose 1-8: 8
Goodbye. You made 3 calculation(s).

How it is built

1. The operations in one dictionary

Each menu number maps to a name and a symbol. The menu, the sums and the history all read from OPERATIONS, so a new operation starts with one new line here.

# Menu number -> (name shown in the menu, symbol shown in the history)
OPERATIONS = {
    "1": ("Add", "+"),
    "2": ("Subtract", "-"),
    "3": ("Multiply", "*"),
    "4": ("Divide", "/"),
    "5": ("Power", "^"),
    "6": ("Remainder", "%"),
}
HISTORY_CHOICE = "7"
QUIT_CHOICE = "8"
MENU_COLUMNS = 4

2. Reading a number safely

float() raises ValueError for text like abc, so the loop catches it and asks again. math.isfinite also turns away inf and nan, which float() would accept.

def read_number(prompt):
    """Ask until the user types an ordinary number."""
    while True:
        text = input(prompt).strip()
        try:
            value = float(text)
        except ValueError:
            print("  That is not a number. Try something like 12 or 3.5.")
            continue
        if math.isfinite(value):
            return value
        print("  Please type an ordinary number.")

3. Doing the sums

Each if returns its answer straight away, so no elif chain is needed. Python writes powers as **; the history shows them as ^.

def calculate(a, b, symbol):
    """Return the result of a <symbol> b."""
    if symbol == "+":
        return a + b
    if symbol == "-":
        return a - b
    if symbol == "*":
        return a * b
    if symbol == "/":
        return a / b
    if symbol == "%":
        return a % b
    return a ** b

4. Catching the impossible

Dividing by zero raises ZeroDivisionError and a huge power raises OverflowError, so both become friendly messages. A finished sum is stored as a line of text in the history list.

def run_operation(choice, history):
    """Ask for two numbers, print the answer and add it to the history."""
    name, symbol = OPERATIONS[choice]
    a = read_number("First number: ")
    b = read_number("Second number: ")
    try:
        result = calculate(a, b, symbol)
    except ZeroDivisionError:
        print("  You cannot divide by zero.")
        return
    except OverflowError:
        print("  That answer is too large to show.")
        return
    # A negative number to a fractional power gives a complex number
    if isinstance(result, complex):
        print("  That power has no real-number answer.")
        return
    if not math.isfinite(result):
        print("  That answer is too large to show.")
        return
    line = f"{format_number(a)} {symbol} {format_number(b)} = {format_number(result)}"
    history.append(line)
    print(f"  {name}: {line}")

5. The main loop

history is created inside main() and handed to the functions that need it. The loop keeps showing the menu until the user picks Quit, and break ends it.

def main():
    """Show the menu and run the user's choices until they quit."""
    history = []
    print("Calculator")
    while True:
        print()
        show_menu()
        choice = input(f"Choose 1-{QUIT_CHOICE}: ").strip()
        if choice in OPERATIONS:
            run_operation(choice, history)
        elif choice == HISTORY_CHOICE:
            show_history(history)
        elif choice == QUIT_CHOICE:
            print(f"Goodbye. You made {len(history)} calculation(s).")
            break
        else:
            print("  Please choose a number from the menu.")

Make it bigger

  • Let the user type ans instead of a number to reuse the last answer.
  • Add square root and percentage as new entries in OPERATIONS.
  • Save the history to a text file when the user quits and load it the next time.
  • Accept a whole sum on one line, such as 12 * 4, and split it into its parts.

What it practises