
1. 为什么用Python写计算器用Python实现四则运算计算器看似简单但这个项目实际上包含了Python编程中多个核心概念的实战应用。作为一门解释型语言Python特别适合快速开发这类工具类程序。我在实际教学中发现90%的Python初学者都会选择计算器作为第一个综合练习项目。这个项目看似基础但完整实现需要处理以下关键点用户输入的安全过滤运算符优先级处理浮点数精度控制异常情况捕获表达式解析算法提示不要小看这个简单项目2017年某知名科技公司的初级工程师面试题就是实现一个支持括号的四则运算计算器很多候选人都在运算符优先级处理上栽了跟头。2. 基础版本实现2.1 最简实现方案我们先从最基础的计算器开始这个版本仅支持两个数的单一运算def basic_calculator(): try: num1 float(input(请输入第一个数字: )) operator input(请输入运算符(-*/): ) num2 float(input(请输入第二个数字: )) if operator : result num1 num2 elif operator -: result num1 - num2 elif operator *: result num1 * num2 elif operator /: if num2 0: raise ValueError(除数不能为零) result num1 / num2 else: raise ValueError(无效的运算符) print(f结果: {result}) except ValueError as e: print(f输入错误: {e}) basic_calculator()这个版本虽然简单但已经包含了几个关键点使用float()进行类型转换除零错误的预防处理基本的异常捕获机制用户输入的基本验证2.2 改进方向分析基础版本有明显局限只能进行两个数的运算不支持表达式输入(如35*2)没有历史记录功能界面过于简陋接下来我们要逐步解决这些问题。但在深入之前需要先理解一个关键概念表达式求值。3. 表达式解析进阶3.1 使用eval的危险与替代方案很多初学者会想到用Python内置的eval函数expression input(请输入表达式: ) result eval(expression) # 危险不要在生产环境使用警告eval会执行任何Python代码如果用户输入import(os).system(rm -rf /)这样的恶意代码将造成严重安全问题。绝对不要在真实项目中使用eval处理用户输入。3.2 安全的表达式解析方案我们需要自己实现表达式解析。常见的有两种方法逆波兰表示法(RPN)先将中缀表达式转换为后缀表达式再用栈计算递归下降解析通过递归函数处理运算符优先级这里我们采用更直观的递归下降方法。核心思路是将表达式分解为项(Term)和因子(Factor)乘除法优先级高于加减法使用递归处理括号import re class Calculator: def __init__(self): self.tokens [] self.pos 0 def evaluate(self, expression): self.tokens re.findall(r\d\.?\d*|[\-*/()], expression) self.pos 0 return self._parse_expression() def _parse_expression(self): result self._parse_term() while self.pos len(self.tokens) and self.tokens[self.pos] in -: op self.tokens[self.pos] self.pos 1 term self._parse_term() result result term if op else result - term return result def _parse_term(self): result self._parse_factor() while self.pos len(self.tokens) and self.tokens[self.pos] in */: op self.tokens[self.pos] self.pos 1 factor self._parse_factor() result result * factor if op * else result / factor return result def _parse_factor(self): token self.tokens[self.pos] if token (: self.pos 1 result self._parse_expression() if self.tokens[self.pos] ! ): raise ValueError(缺少右括号) self.pos 1 return result else: self.pos 1 return float(token) calc Calculator() print(calc.evaluate(35*2)) # 输出13.0 print(calc.evaluate((35)*2)) # 输出16.0这个实现可以正确处理运算符优先级括号嵌套浮点数运算基本的语法错误检查4. 工程化改进4.1 添加历史记录功能实用的计算器应该能记录计算历史class AdvancedCalculator(Calculator): def __init__(self): super().__init__() self.history [] def evaluate(self, expression): try: result super().evaluate(expression) self.history.append(f{expression} {result}) return result except Exception as e: self.history.append(f{expression} → 错误: {str(e)}) raise def show_history(self, limit5): print(\n最近计算记录:) for item in self.history[-limit:]: print(item)4.2 精度控制与显示优化金融计算等场景需要控制小数位数from decimal import Decimal, getcontext class FinancialCalculator(AdvancedCalculator): def __init__(self, precision4): super().__init__() self.precision precision getcontext().prec precision 2 def _parse_factor(self): token self.tokens[self.pos] if token (: return super()._parse_factor() else: self.pos 1 return Decimal(token) def evaluate(self, expression): result super().evaluate(expression) return round(result, self.precision)4.3 添加单元测试确保计算器在各种情况下的正确性import unittest class TestCalculator(unittest.TestCase): def setUp(self): self.calc AdvancedCalculator() def test_basic_operations(self): self.assertEqual(self.calc.evaluate(22), 4) self.assertEqual(self.calc.evaluate(2.5*4), 10) def test_priority(self): self.assertEqual(self.calc.evaluate(23*4), 14) self.assertEqual(self.calc.evaluate((23)*4), 20) def test_division(self): self.assertAlmostEqual(self.calc.evaluate(10/3), 3.333, places3) with self.assertRaises(ZeroDivisionError): self.calc.evaluate(1/0) if __name__ __main__: unittest.main()5. 图形界面实现5.1 使用Tkinter创建GUIimport tkinter as tk from tkinter import messagebox class CalculatorApp: def __init__(self, master): self.master master master.title(Python 计算器) self.entry tk.Entry(master, width30, borderwidth5, font(Arial, 14)) self.entry.grid(row0, column0, columnspan4, padx10, pady10) buttons [ 7, 8, 9, /, 4, 5, 6, *, 1, 2, 3, -, 0, ., , , C, (, ), H ] for i, text in enumerate(buttons): row 1 i // 4 col i % 4 btn tk.Button(master, texttext, padx20, pady10, commandlambda ttext: self.on_button_click(t)) btn.grid(rowrow, columncol, stickynsew) self.calculator AdvancedCalculator() def on_button_click(self, text): if text : try: expression self.entry.get() result self.calculator.evaluate(expression) self.entry.delete(0, tk.END) self.entry.insert(0, str(result)) except Exception as e: messagebox.showerror(错误, str(e)) elif text C: self.entry.delete(0, tk.END) elif text H: history \n.join(self.calculator.history[-5:]) messagebox.showinfo(历史记录, history or 暂无记录) else: self.entry.insert(tk.END, text) if __name__ __main__: root tk.Tk() app CalculatorApp(root) root.mainloop()5.2 界面设计要点布局使用网格布局数字和运算符分区排列字体输入框使用较大字体方便查看响应为按钮添加合适的padding提升点击体验错误处理使用messagebox显示错误信息历史记录通过H键快速查看最近计算6. 常见问题与解决方案6.1 浮点数精度问题问题计算0.1 0.2得到0.30000000000000004解决方案使用Decimal模块设置合适的小数位数最终结果进行四舍五入from decimal import Decimal, getcontext getcontext().prec 6 # 设置6位有效数字 result Decimal(0.1) Decimal(0.2) # 得到Decimal(0.3)6.2 除零错误处理问题用户输入5/0导致程序崩溃解决方案在除法运算前检查除数提供友好的错误提示def safe_divide(a, b): if b 0: raise ValueError(除数不能为零) return a / b6.3 表达式语法验证问题用户输入34等非法表达式解决方案使用正则表达式预验证在解析过程中检查语法def is_valid_expression(expr): pattern r^[\d\-*/().\s]$ # 只允许数字、运算符和括号 return re.match(pattern, expr) is not None7. 性能优化技巧7.1 表达式预编译对于需要重复计算的表达式可以预先编译import re class CompiledCalculator(Calculator): def __init__(self): super().__init__() self.cache {} def evaluate(self, expression): if expression not in self.cache: self.cache[expression] super().evaluate(expression) return self.cache[expression]7.2 多线程计算对于复杂计算可以使用线程防止界面冻结import threading class ThreadedCalculatorApp(CalculatorApp): def calculate_in_thread(self): try: expression self.entry.get() thread threading.Thread( targetlambda: self.show_result(expression), daemonTrue ) thread.start() except Exception as e: messagebox.showerror(错误, str(e)) def show_result(self, expression): result self.calculator.evaluate(expression) self.master.after(0, lambda: self.display_result(result)) def display_result(self, result): self.entry.delete(0, tk.END) self.entry.insert(0, str(result))8. 项目扩展思路8.1 科学计算功能添加常用科学计算函数import math class ScientificCalculator(AdvancedCalculator): def evaluate(self, expression): # 替换函数名 expr expression.replace(sin, math.sin) expr expr.replace(cos, math.cos) expr expr.replace(sqrt, math.sqrt) return super().evaluate(expr)8.2 Web版计算器使用Flask创建Web接口from flask import Flask, request, jsonify app Flask(__name__) calc AdvancedCalculator() app.route(/calculate, methods[POST]) def calculate(): data request.get_json() try: result calc.evaluate(data[expression]) return jsonify({result: result, status: success}) except Exception as e: return jsonify({error: str(e), status: error}), 400 if __name__ __main__: app.run()8.3 移动端应用使用Kivy框架开发跨平台应用from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.textinput import TextInput class CalculatorApp(App): def build(self): self.operators [/, *, , -] self.last_was_operator None self.last_button None self.result TextInput(font_size32, readonlyTrue) layout BoxLayout(orientationvertical) layout.add_widget(self.result) buttons [ [7, 8, 9, /], [4, 5, 6, *], [1, 2, 3, -], [., 0, C, ] ] for row in buttons: h_layout BoxLayout() for label in row: button Button(textlabel, pos_hint{center_x: 0.5, center_y: 0.5}) button.bind(on_pressself.on_button_press) h_layout.add_widget(button) layout.add_widget(h_layout) return layout def on_button_press(self, instance): current self.result.text button_text instance.text if button_text C: self.result.text else: new_text current button_text self.result.text new_text if __name__ __main__: app CalculatorApp() app.run()9. 项目部署与打包9.1 使用PyInstaller打包将GUI应用打包为可执行文件pip install pyinstaller pyinstaller --onefile --windowed calculator_app.py9.2 创建安装程序使用Inno Setup等工具创建Windows安装程序编写ISS脚本文件指定打包后的exe文件路径添加桌面快捷方式设置程序菜单项10. 代码质量保障10.1 静态代码检查使用pylint进行代码质量检查pip install pylint pylint calculator.py10.2 自动化测试扩展测试用例覆盖更多边界情况class ExtendedTests(unittest.TestCase): def test_edge_cases(self): self.assertEqual(self.calc.evaluate(-15), 4) self.assertEqual(self.calc.evaluate(3.5*2), 7) self.assertEqual(self.calc.evaluate(10/4), 2.5) def test_whitespace(self): self.assertEqual(self.calc.evaluate(2 3 * 4), 14) self.assertEqual(self.calc.evaluate(( 2 3 ) * 4 ), 20) def test_invalid_input(self): with self.assertRaises(ValueError): self.calc.evaluate(23) with self.assertRaises(ValueError): self.calc.evaluate(abc)10.3 性能测试使用timeit模块测试关键函数性能import timeit setup from __main__ import AdvancedCalculator calc AdvancedCalculator() expression 35*2/(4-2) print(timeit.timeit(calc.evaluate(expression), setupsetup, number1000))