1. 项目概述
作为一个Python开发者,我经常需要快速验证一些简单的数学运算。虽然系统自带的计算器也能用,但每次都要切换窗口实在麻烦。于是上周我用Python写了个命令行计算器,没想到200行代码就实现了科学计算器级别的功能。今天就把这个项目的完整实现过程分享给大家,特别适合刚学Python的朋友练手。
这个计算器不仅能处理加减乘除,还支持括号优先级、幂运算、三角函数等科学计算功能。核心是利用Python内置的eval()函数实现表达式求值,再通过正则表达式进行输入校验。整个项目涉及字符串处理、异常捕获、用户交互设计等多个Python基础知识点,是巩固编程基础的绝佳案例。
2. 核心功能设计
2.1 基本运算实现
计算器的核心是表达式求值功能。Python的eval()函数可以直接计算字符串形式的数学表达式:
result = eval("2 + 3 * 5") # 输出17但直接使用eval存在安全风险,必须对输入做严格过滤。我采用正则表达式来验证输入合法性:
import re pattern = r'^[\d+\-*/().\s^]+$' # 只允许数字、基本运算符和括号 if not re.match(pattern, user_input): print("包含非法字符!")重要提示:永远不要在生产环境中直接使用eval()执行用户输入。本项目仅作为学习演示,实际应用应该使用更安全的替代方案如ast.literal_eval()。
2.2 科学计算扩展
在基础运算上,我通过math模块添加了科学计算功能:
import math functions = { 'sin': math.sin, 'cos': math.cos, 'tan': math.tan, 'log': math.log10, 'ln': math.log, 'sqrt': math.sqrt }使用时需要先对输入字符串做函数名替换处理:
expr = "sin(30) + log(100)" for func in functions: expr = expr.replace(func, f"math.{func}")2.3 历史记录功能
实用的计算器应该能查看之前的计算记录。我用列表实现了一个简单的历史记录:
history = [] def add_to_history(expr, result): history.append(f"{expr} = {result}") if len(history) > 10: # 限制记录条数 history.pop(0)3. 完整实现代码
3.1 主程序结构
import re import math from datetime import datetime def calculate(expression): # 输入验证 if not validate_input(expression): return None try: # 预处理科学函数 expr = preprocess_functions(expression) # 计算并返回结果 return eval(expr) except Exception as e: print(f"计算错误: {e}") return None def main(): print("Python科学计算器 (输入'quit'退出)") while True: user_input = input(">>> ").strip() if user_input.lower() == 'quit': break if user_input.lower() == 'history': show_history() continue result = calculate(user_input) if result is not None: print(f"结果: {result}") add_to_history(user_input, result) if __name__ == "__main__": main()3.2 输入验证实现
def validate_input(expr): # 移除所有空白字符 clean_expr = re.sub(r'\s+', '', expr) # 基础字符检查 base_pattern = r'^[\d+\-*/().^]+$' if not re.match(base_pattern, clean_expr): print("错误:包含非法字符") return False # 括号匹配检查 stack = [] for char in clean_expr: if char == '(': stack.append(char) elif char == ')': if not stack: print("错误:括号不匹配") return False stack.pop() if stack: print("错误:括号不匹配") return False return True4. 高级功能实现
4.1 变量存储功能
为方便连续计算,我增加了变量存储功能:
variables = {'pi': math.pi, 'e': math.e} def handle_variables(expr): # 替换已知变量 for var in variables: expr = expr.replace(var, str(variables[var])) # 处理赋值语句 if '=' in expr: parts = expr.split('=', 1) var_name = parts[0].strip() if var_name.isidentifier(): value = calculate(parts[1]) if value is not None: variables[var_name] = value return None return expr4.2 单位转换功能
添加常用单位转换支持:
def convert_units(expr): # 角度转弧度 if "deg" in expr: expr = expr.replace("deg", "*math.pi/180") # 其他单位转换... return expr5. 异常处理与调试
5.1 常见错误处理
def safe_calculate(expr): try: return calculate(expr) except ZeroDivisionError: print("错误:除数不能为零") except OverflowError: print("错误:计算结果超出范围") except Exception as e: print(f"计算错误: {type(e).__name__}: {e}") return None5.2 日志记录
添加计算日志方便调试:
def setup_logging(): import logging logging.basicConfig( filename='calculator.log', level=logging.DEBUG, format='%(asctime)s - %(message)s' ) return logging.getLogger() logger = setup_logging() def log_calculation(expr, result): logger.info(f"Expression: {expr} = {result}")6. 项目打包与分发
6.1 使用setuptools打包
创建setup.py文件:
from setuptools import setup setup( name='pycalculator', version='0.1', py_modules=['calculator'], install_requires=[], entry_points={ 'console_scripts': [ 'pcalc=calculator:main', ], }, )6.2 添加命令行参数支持
import argparse def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('-e', '--expression', help='直接计算表达式') return parser.parse_args() if __name__ == "__main__": args = parse_args() if args.expression: result = calculate(args.expression) print(result) else: main()7. 性能优化技巧
7.1 表达式预编译
对于重复计算的表达式,可以使用compile()预编译:
compiled_expr = compile(expr, '<string>', 'eval') result = eval(compiled_expr)7.2 使用缓存机制
对常见计算结果进行缓存:
from functools import lru_cache @lru_cache(maxsize=128) def cached_calculate(expr): return calculate(expr)8. 安全增强方案
8.1 使用ast替代eval
更安全的替代方案:
import ast def safe_eval(expr): try: node = ast.parse(expr, mode='eval') if not all(isinstance(n, (ast.Expression, ast.Constant, ast.UnaryOp, ast.BinOp, ast.Name)) for n in ast.walk(node)): raise ValueError("不安全的表达式") return eval(compile(node, '<string>', 'eval')) except Exception as e: print(f"安全错误: {e}") return None8.2 限制计算复杂度
防止恶意复杂计算:
import resource def set_calculation_limits(): # 限制CPU时间(秒) resource.setrlimit(resource.RLIMIT_CPU, (1, 1)) # 限制内存(MB) resource.setrlimit(resource.RLIMIT_AS, (256*1024*1024, 256*1024*1024))9. 用户界面改进
9.1 彩色输出
使用colorama添加彩色输出:
from colorama import Fore, init init() print(Fore.GREEN + "结果:" + Fore.RESET, result)9.2 交互式自动补全
使用readline实现历史记录搜索:
import readline def setup_readline(): readline.parse_and_bind("tab: complete") readline.set_completer(history_completer) def history_completer(text, state): matches = [h for h in history if h.startswith(text)] return matches[state] if state < len(matches) else None10. 测试与验证
10.1 单元测试示例
import unittest class TestCalculator(unittest.TestCase): def test_basic_operations(self): self.assertEqual(calculate("2+3"), 5) self.assertEqual(calculate("10/2"), 5) def test_functions(self): self.assertAlmostEqual(calculate("sin(0)"), 0) self.assertAlmostEqual(calculate("sqrt(16)"), 4) if __name__ == "__main__": unittest.main()10.2 性能测试
import timeit def test_performance(): setup = "from __main__ import calculate" stmt = "calculate('2**100 + 3**50')" time = timeit.timeit(stmt, setup, number=1000) print(f"1000次计算耗时: {time:.2f}秒")这个项目虽然看起来简单,但涵盖了Python开发的多个重要方面。我在实现过程中特别注重了安全性和可扩展性,代码结构也尽量保持了清晰。对于想深入学习Python的朋友,可以尝试在此基础上添加更多功能,比如图形界面、网络计算服务等。