Calculator Project
Projects Beginner Projects Project

Python Calculator Project

1Calculator Project: overview

A calculator program reads an expression such as 12 * 3, splits it into two numbers and an operator, runs the matching operation and prints the result — reporting bad input instead of crashing.

GoalBy the end you can plan a small program, split it into functions (parse, calculate, main loop), pick an operation with a dictionary, and handle errors such as division by zero and invalid input.

Before this lesson: Python while Loops, Python Dictionaries, Python Functions, Python Exceptions: try, except, else and finally

2Calculator Project: diagram

Python Calculator Projectline = input("calc> ")q quitsparse(line)"12 * 3" -> 12.0, "*", 3.0valid input?no: print ValueError messageyesOPS[op](a, b)choose the operationdivided by zero?yes: print error messagenoprint resultnext line
Each line the user types travels through the same steps: read it, parse it, pick the operation and print the answer. The two decision points are where errors are caught, so the loop can always continue with the next line.

3Calculator Project: explained step by step

A project is mostly planning. Before writing code, decide what the program must do, break the work into steps, and give each step its own function. Then build and test one piece at a time.

Step 1: Write down the requirements

Our calculator should:

  • read a line such as 12 * 3 — a number, an operator, a number;
  • support +, -, * and /;
  • print the answer and wait for the next line, until the user types q;
  • never crash on bad input. Dividing by zero, typing a word instead of a number, or using an unknown operator should each print a clear message.

Step 2: Turn requirements into steps

Every line goes through the same pipeline: read → parse → calculate → print, then repeat. Things can go wrong in two places: while parsing (a bad number or operator) and while calculating (division by zero).

Step 3: One function per job

  • parse(line) splits the text into (number, operator, number) or raises ValueError.
  • calculate(a, op, b) does the maths and returns the result.
  • The main while loop deals with input, output and errors.

Keeping parsing and maths out of the loop means you can test them without typing anything:

Python
def parse(line):
    parts = line.split()
    if len(parts) != 3:
        raise ValueError("expected: number operator number")
    return float(parts[0]), parts[1], float(parts[2])

print(parse("12 * 3"))
print(parse("  7.5   /  2 "))
try:
    parse("12*3")
except ValueError as error:
    print("Rejected:", error)

split() with no argument splits on any run of spaces and ignores spaces at the ends, which is why the messy second line still works.

Step 4: Choose the operation with a dictionary

A long if/elif chain works, but a dictionary that maps each symbol to a function is shorter and easier to extend. The built-in operator module already has the functions you need — operator.add(2, 3) returns 5:

Python
import operator

OPS = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv}
print(OPS["*"](6, 7))
print(sorted(OPS))

Functions are values in Python, so they can be stored in a dictionary and called later. Supporting a new operator becomes a one-line change.

Step 5: Handle errors where they matter

float("ten") raises ValueError, and dividing by zero raises ZeroDivisionError. Rather than predicting every bad input in advance, the main loop wraps parse-and-calculate in try/except, prints a friendly message for each kind of failure, and carries on with the next line.

Quick reference: Calculator Project

4Calculator Project: syntax

import operator

OPS = {"+": operator.add, "-": operator.sub}   # symbol -> function

def parse(line):                 # text -> (a, op, b) or ValueError
    a, op, b = line.split()
    return float(a), op, float(b)

while True:
    line = input("calc> ")
    if line == "q":
        break
    try:
        a, op, b = parse(line)
        print(OPS[op](a, b))
    except ZeroDivisionError:
        print("cannot divide by zero")
    except ValueError as error:
        print("Error:", error)

5Calculator Project: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
import operator

OPS = {"+": operator.add, "-": operator.sub,
       "*": operator.mul, "/": operator.truediv}

def parse(line):
    parts = line.split()
    if len(parts) != 3 or parts[1] not in OPS:
        raise ValueError("write it like 12 * 3 (operators: + - * /)")
    return float(parts[0]), parts[1], float(parts[2])

def calculate(a, op, b):
    return OPS[op](a, b)

while True:
    line = input("calc> ")
    if line == "q":
        break
    try:
        a, op, b = parse(line)
        print(f"= {calculate(a, op, b):g}")
    except ZeroDivisionError:
        print("Error: cannot divide by zero")
    except ValueError as error:
        print("Error:", error)
Expected outputcalc> 12 * 3
= 36
calc> 7 / 2
= 3.5
calc> 5 / 0
Error: cannot divide by zero
calc> ten + 2
Error: could not convert string to float: 'ten'
calc> 4 ^ 2
Error: write it like 12 * 3 (operators: + - * /)
calc> q

7Calculator Project: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1The operator module provides the arithmetic operators as ordinary functions: add, sub, mul and truediv.
  2. Line 3-4OPS maps each symbol to the function that does that maths. Functions are values, so a dictionary can store them.
  3. Line 6-7parse() splits the line on whitespace: "12 * 3" becomes ["12", "*", "3"].
  4. Line 8-9If there are not exactly three pieces, or the middle one is not a known operator, parse() raises ValueError with a hint about the right format.
  5. Line 10float() converts both numbers and the function returns a tuple. float("ten") would raise its own ValueError here.
  6. Line 12-13calculate() looks up the function for op in the dictionary and calls it with a and b.
  7. Line 15-16The loop runs until told to stop. input() shows the prompt calc> and reads one line — on this page, from the Input box.
  8. Line 17-18Typing q ends the loop with break, which ends the program.
  9. Line 19-21The normal path: parse, calculate, print. The :g format drops a pointless .0, so 36.0 prints as 36.
  10. Line 22-235 / 0 raises ZeroDivisionError inside calculate(). It is caught here, a message is printed and the loop continues.
  11. Line 24-25Any ValueError — from the format check or from float() — is caught too, and its message tells the user what went wrong.

8Calculator Project: common mistakes

Doing maths on the strings from input()

✗ Wrong

a = input("First number: ")
b = input("Second number: ")
print("Sum:", a + b)
Typing 2 and 3 prints Sum: 23 — the two strings were joined

✓ Right

a = float(input("First number: "))
b = float(input("Second number: "))
print("Sum:", a + b)

input() always returns a string, and + joins strings. Convert the text with float() (or int()) before doing maths.

Checking for zero after dividing

✗ Wrong

def divide(a, b):
    result = a / b
    if b == 0:
        return "cannot divide by zero"
    return result

print(divide(5.0, 0.0))
ZeroDivisionError: float division by zero

✓ Right

def divide(a, b):
    if b == 0:
        return "cannot divide by zero"
    return a / b

print(divide(5.0, 0.0))

The division runs — and fails — before the check is reached. Test the divisor first, or wrap the division in try/except ZeroDivisionError.

Catching every error with a bare except

✗ Wrong

line = "5 / 0"
try:
    a, op, b = line.split()
    print(float(a) / float(b))
except:
    print("Something went wrong")
Prints Something went wrong — the same vague message for every problem, and even a typo in your own code (a NameError) would be silently hidden

✓ Right

line = "5 / 0"
try:
    a, op, b = line.split()
    print(float(a) / float(b))
except ZeroDivisionError:
    print("Error: cannot divide by zero")
except ValueError as error:
    print("Error:", error)

Catch only the errors you expect, each with its own helpful message. Unexpected bugs then still show a traceback that you can read and fix.

9Calculator Project: exercises

Write your answer, then press Check. The checks run in your browser.

1. Add remainder and power

Write code Easy

Extend the calculator with two new operators: % (remainder) and ** (power). Only the OPS dictionary needs to change — parse() and calculate() pick up new entries automatically. The existing four operators must keep working.

Your code
import operator

OPS = {"+": operator.add, "-": operator.sub,
       "*": operator.mul, "/": operator.truediv}

def parse(line):
    parts = line.split()
    if len(parts) != 3 or parts[1] not in OPS:
        raise ValueError("write it like 12 * 3")
    return float(parts[0]), parts[1], float(parts[2])

def calculate(a, op, b):
    return OPS[op](a, b)

print(calculate(10, "%", 3))
print(calculate(2, "**", 5))

2. Predict the error handling

Predict the output Medium

Read the code and type exactly what it prints. Think about which error each input causes and where it is caught.

Read this code
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "undefined"

for text in ["8 / 2", "3 / 0", "x / 2"]:
    try:
        left, op, right = text.split()
        print(safe_divide(float(left), float(right)))
    except ValueError:
        print("bad number")

2 more exercises → 4 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

Why not just use eval() to calculate the expression?

eval() runs whatever Python code it receives, so a user could type something harmful instead of a sum, and a small typo produces a confusing SyntaxError. Parsing the text yourself and allowing only known operators is safer, and it teaches you how real programs turn text into actions.

How do I give the calculator input on this site?

Type the lines into the Input (stdin) box before pressing Run — one line per input() call, ending with q. The program echoes each line after the calc> prompt, just like a terminal.

Why does 0.1 + 0.2 not give exactly 0.3?

Floats are stored in binary, so most decimals are tiny approximations and 0.1 + 0.2 is really 0.30000000000000004. The :g format shows six significant digits, so the calculator prints 0.3. For money, round the result or use the decimal module.

Further reading

10Next lesson

Up nextPython Expense Tracker ProjectAn expense tracker stores each purchase as a dictionary inside a list, adds new entries, totals the spending per catego…

Related:
Python Input and OutputPython FunctionsPython Exceptions: try, except, else and finallyPython Expense Tracker Project

← Matplotlib Chart Types · All Beginner Projects lessons · Practise in the IDE