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.
Before this lesson: Python while Loops, Python Dictionaries, Python Functions, Python Exceptions: try, except, else and finally
2Calculator Project: diagram
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 raisesValueError.calculate(a, op, b)does the maths and returns the result.- The main
whileloop deals with input, output and errors.
Keeping parsing and maths out of the loop means you can test them without typing anything:
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:
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
- Python Simple Calculator Program — A simple Python calculator reads two numbers and an operator with `input()`, then uses `if/elif` (or a dictio…
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.
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.
- Line 1The
operatormodule provides the arithmetic operators as ordinary functions:add,sub,mulandtruediv. - Line 3-4
OPSmaps each symbol to the function that does that maths. Functions are values, so a dictionary can store them. - Line 6-7
parse()splits the line on whitespace:"12 * 3"becomes["12", "*", "3"]. - Line 8-9If there are not exactly three pieces, or the middle one is not a known operator,
parse()raisesValueErrorwith a hint about the right format. - Line 10
float()converts both numbers and the function returns a tuple.float("ten")would raise its ownValueErrorhere. - Line 12-13
calculate()looks up the function foropin the dictionary and calls it withaandb. - Line 15-16The loop runs until told to stop.
input()shows the promptcalc>and reads one line — on this page, from the Input box. - Line 17-18Typing
qends the loop withbreak, which ends the program. - Line 19-21The normal path: parse, calculate, print. The
:gformat drops a pointless.0, so36.0prints as36. - Line 22-23
5 / 0raisesZeroDivisionErrorinsidecalculate(). It is caught here, a message is printed and the loop continues. - Line 24-25Any
ValueError— from the format check or fromfloat()— 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)✓ 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))✓ 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")✓ 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 ✓ CompletedExtend 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.
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))import operator
OPS = {"+": operator.add, "-": operator.sub,
"*": operator.mul, "/": operator.truediv,
"%": operator.mod, "**": operator.pow}
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 ✓ CompletedRead the code and type exactly what it prints. Think about which error each input causes and where it is caught.
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")4.0
undefined
bad number
2 more exercises → 4 more examples
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