1. 项目概述
今天我们来开发一个Python版的简单计算器程序。这个看似基础的项目实际上包含了Python编程中许多核心概念,非常适合作为Python入门学习的第一个实战项目。通过这个项目,我们可以掌握用户输入处理、条件判断、异常处理等关键编程技能。
这个计算器程序将实现基本的加减乘除运算功能,同时具备良好的用户交互体验和错误处理机制。虽然功能简单,但我们会从工程化的角度来完善它,让它成为一个健壮、实用的工具。
2. 核心功能实现
2.1 基础计算功能
我们先来看计算器的核心计算逻辑实现:
python复制def calculator():
print("=====简单计算器V1.0=====")
try:
num1 = float(input("请输入第一个数字:"))
operation = input("请输入操作符:")
num2 = float(input("请输入第二个数字:"))
# 执行计算
if operation == "+":
result = num1 + num2
elif operation == "-":
result = num1 - num2
elif operation == "*":
result = num1 * num2
elif operation == "/":
result = num1 / num2
else:
print("不支持的操作类型")
return
# 显示结果
print(f"{num1}{operation}{num2} = {result}")
except ValueError:
print("请输入有效数字")
这段代码实现了计算器的基本功能:
- 通过
input()函数获取用户输入 - 使用
float()将输入转换为浮点数 - 通过
if-elif条件判断执行不同的运算 - 使用
try-except捕获可能的输入错误
提示:使用
float()而不是int()可以让计算器支持小数运算,提高实用性。
2.2 代码结构优化
虽然上面的代码可以工作,但我们可以做一些改进:
python复制def calculate(num1, operation, num2):
"""执行实际计算"""
if operation == "+":
return num1 + num2
elif operation == "-":
return num1 - num2
elif operation == "*":
return num1 * num2
elif operation == "/":
if num2 == 0:
raise ValueError("除数不能为零")
return num1 / num2
else:
raise ValueError("不支持的操作类型")
def calculator():
print("=====简单计算器V1.0=====")
try:
num1 = float(input("请输入第一个数字:"))
operation = input("请输入操作符(+-*/):")
num2 = float(input("请输入第二个数字:"))
result = calculate(num1, operation, num2)
print(f"{num1}{operation}{num2} = {result}")
except ValueError as e:
print(f"输入错误:{e}")
except ZeroDivisionError:
print("错误:除数不能为零")
改进点:
- 将计算逻辑单独封装为
calculate()函数 - 增加了除数为零的检查
- 错误处理更加细致
- 操作符提示更明确
3. 功能扩展与增强
3.1 支持连续计算
基础版本每次只能执行一次计算就退出,我们可以让它支持连续计算:
python复制def calculator():
print("=====简单计算器V1.1=====")
print("输入'q'退出程序")
while True:
try:
user_input = input("请输入第一个数字(或q退出):")
if user_input.lower() == 'q':
break
num1 = float(user_input)
operation = input("请输入操作符(+-*/):")
num2 = float(input("请输入第二个数字:"))
result = calculate(num1, operation, num2)
print(f"{num1}{operation}{num2} = {result}")
except ValueError as e:
print(f"输入错误:{e}")
except ZeroDivisionError:
print("错误:除数不能为零")
现在计算器会一直运行,直到用户输入'q'退出。
3.2 增加历史记录功能
我们可以添加一个简单的计算历史记录功能:
python复制calculation_history = []
def calculator():
print("=====简单计算器V1.2=====")
print("输入'q'退出,'h'查看历史记录")
while True:
try:
user_input = input("请输入第一个数字(或q/h):")
if user_input.lower() == 'q':
break
elif user_input.lower() == 'h':
print("\n===计算历史===")
for item in calculation_history:
print(item)
print("===============\n")
continue
num1 = float(user_input)
operation = input("请输入操作符(+-*/):")
num2 = float(input("请输入第二个数字:"))
result = calculate(num1, operation, num2)
calculation = f"{num1}{operation}{num2} = {result}"
calculation_history.append(calculation)
print(calculation)
except ValueError as e:
print(f"输入错误:{e}")
except ZeroDivisionError:
print("错误:除数不能为零")
现在用户可以输入'h'查看之前的计算记录。
4. 异常处理与输入验证
4.1 强化输入验证
我们可以编写专门的输入验证函数:
python复制def get_number(prompt):
"""获取并验证数字输入"""
while True:
try:
value = input(prompt)
if value.lower() in ('q', 'h'):
return value
return float(value)
except ValueError:
print("请输入有效数字")
def get_operation(prompt):
"""获取并验证操作符输入"""
valid_operations = '+-*/'
while True:
operation = input(prompt)
if operation in valid_operations:
return operation
print(f"无效操作符,请使用{valid_operations}之一")
然后在主函数中使用这些验证函数:
python复制def calculator():
print("=====简单计算器V1.3=====")
print("输入'q'退出,'h'查看历史记录")
while True:
num1 = get_number("请输入第一个数字(或q/h):")
if num1 == 'q':
break
if num1 == 'h':
show_history()
continue
operation = get_operation("请输入操作符(+-*/):")
num2 = get_number("请输入第二个数字:")
if num2 == 'q':
break
if num2 == 'h':
show_history()
continue
try:
result = calculate(num1, operation, num2)
calculation = f"{num1}{operation}{num2} = {result}"
calculation_history.append(calculation)
print(calculation)
except ZeroDivisionError:
print("错误:除数不能为零")
4.2 自定义异常
为了更好的错误处理,我们可以定义自己的异常类:
python复制class CalculatorError(Exception):
"""计算器异常基类"""
pass
class InvalidOperationError(CalculatorError):
"""无效操作符异常"""
pass
class DivisionByZeroError(CalculatorError):
"""除零异常"""
pass
def calculate(num1, operation, num2):
"""执行实际计算"""
if operation == "+":
return num1 + num2
elif operation == "-":
return num1 - num2
elif operation == "*":
return num1 * num2
elif operation == "/":
if num2 == 0:
raise DivisionByZeroError("除数不能为零")
return num1 / num2
else:
raise InvalidOperationError("不支持的操作类型")
5. 代码重构与模块化
5.1 将计算器类化
我们可以将计算器功能封装成一个类:
python复制class SimpleCalculator:
def __init__(self):
self.history = []
def calculate(self, num1, operation, num2):
"""执行实际计算"""
if operation == "+":
return num1 + num2
elif operation == "-":
return num1 - num2
elif operation == "*":
return num1 * num2
elif operation == "/":
if num2 == 0:
raise DivisionByZeroError("除数不能为零")
return num1 / num2
else:
raise InvalidOperationError("不支持的操作类型")
def run(self):
"""运行计算器"""
print("=====简单计算器V1.4=====")
print("输入'q'退出,'h'查看历史记录")
while True:
try:
num1 = self.get_number("请输入第一个数字(或q/h):")
if num1 == 'q':
break
if num1 == 'h':
self.show_history()
continue
operation = self.get_operation("请输入操作符(+-*/):")
num2 = self.get_number("请输入第二个数字:")
if num2 == 'q':
break
if num2 == 'h':
self.show_history()
continue
result = self.calculate(num1, operation, num2)
calculation = f"{num1}{operation}{num2} = {result}"
self.history.append(calculation)
print(calculation)
except CalculatorError as e:
print(f"计算错误:{e}")
def get_number(self, prompt):
"""获取并验证数字输入"""
while True:
try:
value = input(prompt)
if value.lower() in ('q', 'h'):
return value
return float(value)
except ValueError:
print("请输入有效数字")
def get_operation(self, prompt):
"""获取并验证操作符输入"""
valid_operations = '+-*/'
while True:
operation = input(prompt)
if operation in valid_operations:
return operation
print(f"无效操作符,请使用{valid_operations}之一")
def show_history(self):
"""显示计算历史"""
print("\n===计算历史===")
for item in self.history:
print(item)
print("===============\n")
if __name__ == "__main__":
calc = SimpleCalculator()
calc.run()
5.2 添加单元测试
为了保证代码质量,我们应该为计算器添加单元测试:
python复制import unittest
class TestSimpleCalculator(unittest.TestCase):
def setUp(self):
self.calc = SimpleCalculator()
def test_addition(self):
self.assertEqual(self.calc.calculate(2, '+', 3), 5)
self.assertEqual(self.calc.calculate(-1, '+', 1), 0)
self.assertEqual(self.calc.calculate(0.5, '+', 0.5), 1.0)
def test_subtraction(self):
self.assertEqual(self.calc.calculate(5, '-', 3), 2)
self.assertEqual(self.calc.calculate(0, '-', 1), -1)
def test_multiplication(self):
self.assertEqual(self.calc.calculate(2, '*', 3), 6)
self.assertEqual(self.calc.calculate(-1, '*', 5), -5)
def test_division(self):
self.assertEqual(self.calc.calculate(6, '/', 3), 2)
self.assertEqual(self.calc.calculate(1, '/', 2), 0.5)
def test_division_by_zero(self):
with self.assertRaises(DivisionByZeroError):
self.calc.calculate(1, '/', 0)
def test_invalid_operation(self):
with self.assertRaises(InvalidOperationError):
self.calc.calculate(1, 'x', 2)
if __name__ == "__main__":
unittest.main()
6. 图形界面版本
6.1 使用Tkinter创建GUI
我们可以使用Python内置的Tkinter库为计算器添加图形界面:
python复制import tkinter as tk
from tkinter import messagebox
class CalculatorGUI:
def __init__(self, master):
self.master = master
master.title("简单计算器")
self.create_widgets()
self.calculator = SimpleCalculator()
def create_widgets(self):
# 输入框
self.entry = tk.Entry(self.master, width=25, font=('Arial', 14))
self.entry.grid(row=0, column=0, columnspan=4, pady=10)
# 按钮布局
buttons = [
'7', '8', '9', '/',
'4', '5', '6', '*',
'1', '2', '3', '-',
'0', '.', '=', '+',
'C', 'H'
]
row = 1
col = 0
for button in buttons:
cmd = lambda x=button: self.on_button_click(x)
tk.Button(self.master, text=button, width=5, height=2,
command=cmd).grid(row=row, column=col, padx=2, pady=2)
col += 1
if col > 3:
col = 0
row += 1
def on_button_click(self, char):
if char == '=':
try:
expression = self.entry.get()
# 简单的表达式解析
if '+' in expression:
parts = expression.split('+')
result = self.calculator.calculate(float(parts[0]), '+', float(parts[1]))
elif '-' in expression:
parts = expression.split('-')
result = self.calculator.calculate(float(parts[0]), '-', float(parts[1]))
elif '*' in expression:
parts = expression.split('*')
result = self.calculator.calculate(float(parts[0]), '*', float(parts[1]))
elif '/' in expression:
parts = expression.split('/')
result = self.calculator.calculate(float(parts[0]), '/', float(parts[1]))
else:
result = expression
self.entry.delete(0, tk.END)
self.entry.insert(tk.END, str(result))
self.calculator.history.append(f"{expression} = {result}")
except CalculatorError as e:
messagebox.showerror("错误", str(e))
except Exception:
messagebox.showerror("错误", "无效的表达式")
elif char == 'C':
self.entry.delete(0, tk.END)
elif char == 'H':
history = "\n".join(self.calculator.history[-10:]) if self.calculator.history else "无历史记录"
messagebox.showinfo("计算历史", history)
else:
self.entry.insert(tk.END, char)
if __name__ == "__main__":
root = tk.Tk()
app = CalculatorGUI(root)
root.mainloop()
6.2 GUI版本改进
我们可以改进GUI版本,使其更加用户友好:
python复制class ImprovedCalculatorGUI(CalculatorGUI):
def create_widgets(self):
# 输入框和结果显示框分开
self.expression_var = tk.StringVar()
self.result_var = tk.StringVar()
tk.Label(self.master, text="表达式:").grid(row=0, column=0, sticky='e')
self.entry = tk.Entry(self.master, textvariable=self.expression_var,
width=25, font=('Arial', 12))
self.entry.grid(row=0, column=1, columnspan=3, pady=5)
tk.Label(self.master, text="结果:").grid(row=1, column=0, sticky='e')
result_label = tk.Label(self.master, textvariable=self.result_var,
width=25, font=('Arial', 14), anchor='e', relief='sunken')
result_label.grid(row=1, column=1, columnspan=3, pady=5)
# 按钮布局
buttons = [
('7', '8', '9', '/', 'C'),
('4', '5', '6', '*', '⌫'),
('1', '2', '3', '-', 'H'),
('0', '.', '=', '+', '')
]
for row_idx, row_buttons in enumerate(buttons, start=2):
for col_idx, button_text in enumerate(row_buttons):
if button_text:
cmd = lambda x=button_text: self.on_button_click(x)
tk.Button(self.master, text=button_text, width=5, height=2,
command=cmd).grid(row=row_idx, column=col_idx, padx=2, pady=2)
def on_button_click(self, char):
current = self.expression_var.get()
if char == '=':
try:
expression = current
result = eval(expression) # 使用eval简化计算,实际项目中要注意安全风险
self.result_var.set(result)
self.calculator.history.append(f"{expression} = {result}")
except Exception as e:
messagebox.showerror("错误", f"计算错误: {e}")
elif char == 'C':
self.expression_var.set('')
self.result_var.set('')
elif char == '⌫':
self.expression_var.set(current[:-1])
elif char == 'H':
history = "\n".join(self.calculator.history[-10:]) if self.calculator.history else "无历史记录"
messagebox.showinfo("计算历史", history)
else:
self.expression_var.set(current + char)
注意:实际项目中直接使用eval()存在安全风险,这里仅作演示用途。生产环境应该使用更安全的表达式解析方法。
7. 项目总结与进阶方向
通过这个简单的计算器项目,我们学习了Python的多个重要概念:
- 基础语法:变量、函数、条件判断
- 用户输入处理与验证
- 异常处理机制
- 代码重构与模块化
- 面向对象编程
- 单元测试编写
- 图形界面开发
这个项目还可以进一步扩展:
- 添加科学计算功能(平方根、幂运算等)
- 支持更复杂的表达式计算(如括号优先级)
- 添加主题切换功能
- 实现计算结果的复制功能
- 添加计算结果的单位转换
- 支持计算结果的图表展示
计算器虽然简单,但通过不断迭代和完善,可以成为一个展示Python编程能力的完整项目。建议读者尝试自己实现一些扩展功能,这将大大提升你的Python编程能力。