20243105 2025-2026-2 《Python程序设计》实验二报告
20243105 2025-2026-2 《Python程序设计》实验2报告
课程:《Python程序设计》
班级:2431
姓名:代雨航
学号:20243105
实验教师:王志强
实验日期:2026年4月13日
必修/选修:公选课
1. 实验内容
1.1 编写计算器程序
设计并完成一个完整的应用程序,完成加减乘除模等运算,功能多多益善。
考核基本语法、判定语句、循环语句、逻辑运算等知识点。
1.2 用LLM生成一个计算器程序
使用大语言模型(LLM)生成一个功能更全面的计算器程序,介绍相关功能,并分析生成的程序代码含义。对比分析自写程序与生成程序的区别(好与坏)。
2. 实验过程及结果
本实验代码已提交到Gitee,链接:[请填写你的Gitee仓库链接]
2.1 自写计算器程序
2.1.1 程序设计思路
我设计了一个菜单驱动的简易计算器,用户通过输入数字选择运算类型,然后分别输入两个操作数完成计算。主要功能包括:
- 加法(+)、减法(-)、乘法(*)、除法(/)
- 取模运算(%)
- 幂运算(**)
- 整除运算(//)
程序包含以下核心结构:
show_menu()函数:打印操作菜单,展示所有支持的运算类型get_number(prompt_text)函数:获取用户输入数字,利用try-except捕获ValueError异常,确保输入有效性calculate()主函数:包含一个while True无限循环,通过if-elif分支判断用户选择并执行对应运算- 除法、取模、整除运算前都会检查除数是否为零,避免程序崩溃
- 使用
count变量统计计算次数,退出时显示

2.1.2 完整代码
# 简易计算器程序
# 支持加减乘除、取模、幂运算、整除
def show_menu():
"""显示功能菜单"""
print("=" * 30)
print(" 简易计算器")
print("=" * 30)
print("1. 加法 (+)")
print("2. 减法 (-)")
print("3. 乘法 (*)")
print("4. 除法 (/)")
print("5. 取模 (%)")
print("6. 幂运算 (**)")
print("7. 整除 (//)")
print("0. 退出程序")
print("-" * 30)
def get_number(prompt_text):
"""获取用户输入的数字,带错误处理"""
while True:
try:
num = float(input(prompt_text))
return num
except ValueError:
print("输入错误,请输入一个有效的数字!")
def calculate():
"""主计算逻辑"""
count = 0 # 记录计算次数
while True:
show_menu()
# 获取用户选择
choice = input("请选择运算 (0-7): ").strip()
# 退出判断
if choice == '0':
print(f"本次共进行了 {count} 次计算,再见!")
break
# 验证选择是否有效
if choice not in ['1', '2', '3', '4', '5', '6', '7']:
print("无效选择,请重新输入!")
continue
# 获取两个操作数
num1 = get_number("请输入第一个数字: ")
num2 = get_number("请输入第二个数字: ")
# 根据选择执行相应运算
if choice == '1':
result = num1 + num2
op = '+'
elif choice == '2':
result = num1 - num2
op = '-'
elif choice == '3':
result = num1 * num2
op = '*'
elif choice == '4':
if num2 == 0:
print("错误:除数不能为零!")
continue
result = num1 / num2
op = '/'
elif choice == '5':
if num2 == 0:
print("错误:取模运算除数不能为零!")
continue
result = num1 % num2
op = '%'
elif choice == '6':
result = num1 ** num2
op = '**'
elif choice == '7':
if num2 == 0:
print("错误:整除运算除数不能为零!")
continue
result = num1 // num2
op = '//'
# 输出结果
print(f"\n计算结果: {num1} {op} {num2} = {result}")
count += 1
print(f"(已累计计算 {count} 次)\n")
# 程序入口
if __name__ == "__main__":
print("欢迎使用简易计算器!")
print("本程序支持: 加、减、乘、除、取模、幂运算、整除\n")
calculate()
2.1.3 代码说明
代码首先定义了 show_menu() 函数来显示菜单,让用户清楚知道支持哪些运算。get_number() 函数用 while True 循环配合 try-except 来保证用户输入的一定是个数字,输入错误会提示重新输入。
主函数 calculate() 里的核心逻辑是:
- 用一个
while True循环不断接受用户输入 - 用户选择
0时用break退出循环,结束程序 - 用
if choice not in [...]逻辑判断来做输入验证 - 根据
choice的值,通过if-elif多分支结构匹配到对应的运算 - 对于除法、取模、整除三种运算,在执行前用
if num2 == 0判断除数,避免程序报错崩溃 - 每次成功计算后
count += 1记录次数
这个程序主要运用了:变量定义、函数定义、while 循环、if-elif-else 分支、try-except 异常处理、break/continue 流程控制、列表 in 成员判断等基础语法。

2.2 LLM生成的计算器程序
2.2.1 程序功能介绍
我通过LLM生成了一个功能更丰富的"高级多功能计算器"。这个计算器不仅支持基础的加减乘除,还具备以下高级功能:
| 功能类别 | 具体功能 |
|---|---|
| 基础运算 | +、-、*、/、%、**、// |
| 科学计算 | sin、cos、tan(支持角度/弧度切换)、sqrt(开方)、log10(常用对数)、ln(自然对数)、exp(指数)、abs(绝对值) |
| 特殊运算 | 阶乘(!)、平方sq()、立方cb()、百分比(%) |
| 统计功能 | avg()求平均、sum()求和、max()最大值、min()最小值 |
| 进制转换 | bin()转二进制、oct()转八进制、hex()转十六进制 |
| 辅助功能 | 历史记录(hist)、内存存储(m+/mr/mc)、上条结果(ans)、清屏(clear)、帮助(help) |
| 常量 | pi(圆周率)、e(自然常数) |

2.2.2 完整代码
# 高级多功能计算器 (LLM生成)
# 功能:基础运算、科学计算、统计、进制转换、历史记录、内存存储
import math
import sys
from datetime import datetime
class AdvancedCalculator:
"""高级计算器类,封装所有计算功能"""
def __init__(self):
self.history_list = [] # 计算历史记录
self.memory_value = 0.0 # 内存存储值
self.has_memory = False # 是否已存储内存值
self.angle_mode = "deg" # 角度模式: deg(角度) / rad(弧度)
self.last_result = 0.0 # 保存上一条计算结果
# ------------- 辅助方法 -------------
def _record(self, expression, result):
"""将计算记录添加进历史列表,最多保留15条"""
now = datetime.now().strftime("%H:%M:%S")
record = f"[{now}] {expression} => {result}"
self.history_list.append(record)
if len(self.history_list) > 15:
self.history_list.pop(0)
def get_input(self, prompt=">>> "):
"""获取用户输入,封装异常处理"""
return input(prompt).strip()
# ------------- 显示方法 -------------
def print_banner(self):
"""显示启动横幅"""
print("\n" + "=" * 56)
print(" 高级多功能计算器 v1.0")
print("=" * 56)
def print_menu(self):
"""显示功能菜单"""
print("""
┌──────────────────────────────────────────────────────┐
│ 【基础运算】 + - * / % ** // │
│ 【科学计算】 sin cos tan sqrt log10 ln exp abs │
│ 【特殊运算】 !(阶乘) sq(平方) cb(立方) %(百分比) │
│ 【统计功能】 avg(平均) sum(求和) max min │
│ 【进制转换】 bin oct hex │
│ │
│ 常量: pi(圆周率) e(自然常数) │
│ 命令: ans(上条结果) m+(存储) mr(读取) mc(清除) │
│ hist(历史) clear(清屏) help(帮助) │
│ quit/q/exit(退出) │
└──────────────────────────────────────────────────────┘""")
def print_help(self):
"""显示帮助信息"""
msg = """
======== 使用说明 ========
1. 基础运算: 直接输入表达式,如 2+3*4
2. 科学函数: sin(30), cos(60), sqrt(16), log10(100), ln(e)
角度模式默认使用"度"(deg),可输入 `mode` 切换为弧度(rad)
3. 阶乘: 5! 或 fact(5) 计算 5 的阶乘
4. 平方/立方: sq(4)=16, cb(3)=27
5. 统计: avg(1,2,3) 求平均, sum(1,2,3) 求和
6. 进制: bin(10) 转二进制, oct(10) 转八进制, hex(255) 转十六进制
7. 内存: m+5 存储5, mr 读取内存, mc 清除内存
8. ans: 使用上一条计算结果, 如 ans+10
========================
"""
print(msg)
def print_history(self):
"""显示计算历史"""
if not self.history_list:
print("\n暂无历史记录。")
return
print("\n" + "-" * 56)
print("计算历史记录 (最新在上):")
print("-" * 56)
for item in reversed(self.history_list):
print(" ", item)
print("-" * 56)
# ------------- 表达式解析 -------------
def evaluate(self, expr):
"""解析并计算用户输入的表达式"""
expr = expr.strip()
# 空输入
if not expr:
return None
# 退出命令
if expr.lower() in ('quit', 'q', 'exit'):
print("感谢使用,再见!")
sys.exit(0)
# 功能命令
if expr.lower() == 'help':
self.print_help()
return None
if expr.lower() == 'hist':
self.print_history()
return None
if expr.lower() == 'clear':
print("\n" * 40)
return None
if expr.lower() == 'mode':
self.angle_mode = "rad" if self.angle_mode == "deg" else "deg"
print(f"角度模式已切换为: {self.angle_mode}")
return None
if expr.lower() == 'pi':
result = math.pi
print(f"π = {result}")
self.last_result = result
return result
if expr.lower() == 'e':
result = math.e
print(f"e = {result}")
self.last_result = result
return result
# 内存操作
if expr.lower() == 'mr':
if self.has_memory:
print(f"内存值: {self.memory_value}")
self.last_result = self.memory_value
return self.memory_value
else:
print("内存为空,请先使用 m+ 存储数据。")
return None
if expr.lower() == 'mc':
self.memory_value = 0.0
self.has_memory = False
print("内存已清除。")
return None
if expr.lower().startswith('m+'):
try:
val = float(expr[2:].strip())
self.memory_value = val
self.has_memory = True
print(f"已存储: {val}")
except ValueError:
print("m+ 格式错误,示例: m+3.14")
return None
# 替换 ans 为上一条结果
expr = expr.replace('ans', str(self.last_result))
# 处理带括号的函数调用
try:
result = self._parse_expression(expr)
if result is not None:
self.last_result = result
self._record(expr, result)
return result
except ZeroDivisionError:
print("错误: 除数不能为零!")
return None
except OverflowError:
print("错误: 计算结果溢出!")
return None
except Exception as e:
print(f"计算错误: {e}")
return None
def _parse_expression(self, expr):
"""内部分支解析,按优先级匹配各种运算"""
e = expr.lower().strip()
# --- 阶乘 ---
if e.endswith('!'):
try:
n = int(e[:-1])
if n < 0:
print("错误: 阶乘不支持负数")
return None
return math.factorial(n)
except ValueError:
print("错误: 阶乘需要整数输入")
return None
# --- 科学函数 (带括号) ---
funcs = [
('sin(', 4, lambda x: math.sin(math.radians(x) if self.angle_mode == 'deg' else x)),
('cos(', 4, lambda x: math.cos(math.radians(x) if self.angle_mode == 'deg' else x)),
('tan(', 4, lambda x: math.tan(math.radians(x) if self.angle_mode == 'deg' else x)),
('sqrt(', 5, math.sqrt),
('log10(', 6, math.log10),
('ln(', 3, math.log),
('exp(', 4, math.exp),
('abs(', 4, abs),
('fact(', 5, lambda x: math.factorial(int(x)) if x >= 0 and x == int(x) else (_ for _ in ()).throw(ValueError("阶乘需要非负整数"))),
]
for prefix, offset, func in funcs:
if e.startswith(prefix) and e.endswith(')'):
inner = e[offset:-1].strip()
arg = float(inner)
return func(arg)
# --- 统计函数 ---
if e.startswith('avg(') and e.endswith(')'):
nums = self._parse_num_list(e[4:-1])
return sum(nums) / len(nums) if nums else None
if e.startswith('sum(') and e.endswith(')'):
nums = self._parse_num_list(e[4:-1])
return sum(nums) if nums else None
if e.startswith('max(') and e.endswith(')'):
nums = self._parse_num_list(e[4:-1])
return max(nums) if nums else None
if e.startswith('min(') and e.endswith(')'):
nums = self._parse_num_list(e[5:-1])
return min(nums) if nums else None
# --- 平方/立方 ---
if e.startswith('sq(') and e.endswith(')'):
val = float(e[3:-1])
return val ** 2
if e.startswith('cb(') and e.endswith(')'):
val = float(e[3:-1])
return val ** 3
# --- 百分比 ---
if e.endswith('%'):
val = float(e[:-1])
return val / 100
# --- 进制转换 ---
if e.startswith('bin(') and e.endswith(')'):
return bin(int(float(e[4:-1])))
if e.startswith('oct(') and e.endswith(')'):
return oct(int(float(e[4:-1])))
if e.startswith('hex(') and e.endswith(')'):
return hex(int(float(e[4:-1])))
# --- 基础运算 (使用eval,带安全检查) ---
allowed = set("0123456789+-*/.%() e")
if not all(c in allowed for c in e):
raise ValueError(f"表达式中含有不允许的字符: {e}")
# 将 ^ 转为 ** 方便幂运算
e = e.replace('^', '**')
return eval(e)
def _parse_num_list(self, s):
"""解析逗号分隔的数字列表"""
return [float(x.strip()) for x in s.split(',') if x.strip()]
# ------------- 主循环 -------------
def run(self):
"""启动计算器主循环"""
self.print_banner()
self.print_menu()
while True:
try:
user_input = self.get_input()
result = self.evaluate(user_input)
if result is not None:
if user_input.startswith(('bin(', 'oct(', 'hex(')):
print(f"结果: {result}")
elif user_input.strip().lower() in ('pi', 'e'):
pass # 已自行打印
else:
print(f"结果: {result}")
except KeyboardInterrupt:
print("\n\n程序已终止,再见!")
sys.exit(0)
except EOFError:
print("\n检测到输入结束,退出程序。")
sys.exit(0)
# ======== 主程序入口 ========
if __name__ == "__main__":
print("正在初始化计算器...")
app = AdvancedCalculator()
app.run()
2.2.3 代码分析
这个LLM生成的代码采用了面向对象的设计,定义了一个 AdvancedCalculator 类来组织所有功能。
类的初始化 __init__:在构造函数中定义了 history_list(历史记录列表)、memory_value(内存值)、has_memory(内存标记)、angle_mode(角度/弧度模式)和 last_result(上一条结果)五个属性,这使得计算器可以在运算过程中记住状态。
核心解析函数 evaluate():这是整个程序的大脑。它接收用户输入的字符串,然后通过一系列 if 语句来判断输入属于哪种类型:
- 先判断是否为空、是否为退出/帮助/历史/清屏等功能命令
- 再判断是否为内存操作(m+/mr/mc)
- 将字符串中的
ans替换为last_result的值,实现"使用上一条结果"的功能 - 最后调用
_parse_expression()做具体的数学解析
_parse_expression() 函数:这个函数使用了"按优先级匹配"的策略。先检查是否是阶乘(以 ! 结尾),然后遍历预定义的科学函数列表 funcs,每个函数都记录了前缀字符串、偏移量和对应的计算函数。这种设计的好处是添加新函数很方便,只需要在列表里加一条就行。对于基础运算,最后使用了 eval() 函数,但在一开始做了安全检查——用 allowed 字符集过滤,确保只允许数字和运算符,防止注入。
错误处理:程序在多个层面做了错误处理。evaluate() 中用 try-except 捕获了 ZeroDivisionError、OverflowError 和通用的 Exception。run() 主循环中额外处理了 KeyboardInterrupt(Ctrl+C)和 EOFError(输入流结束)。

2.3 自写程序与LLM生成程序的对比分析
对比表格
| 对比维度 | 自写程序 | LLM生成程序 |
|---|---|---|
| 代码行数 | 约70行 | 约220行 |
| 设计方式 | 面向过程(函数式) | 面向对象(类封装) |
| 功能数量 | 7种基础运算 | 30+种运算和辅助功能 |
| 错误处理 | 3处(输入验证+除零检查) | 多处(除零、溢出、非法字符、EOF、键盘中断等) |
| 用户界面 | 简单菜单选择 | 丰富菜单+帮助系统+历史记录 |
| 状态管理 | 仅统计次数 | 内存存储、历史记录、上条结果、角度模式 |
| 输入方式 | 两步输入(先选运算再输数字) | 一步输入(直接输表达式或函数) |
| 第三方库依赖 | 无 | math、sys、datetime |
自写程序的优点
- 代码短小精悍,逻辑清晰,容易理解。一个初学者也能很快看懂每一行在做什么
- 没有额外的库依赖,只用了Python内置功能
- 程序启动快,不需要初始化任何数据结构
- 菜单式交互降低了用户的学习成本,不需要记忆命令格式
自写程序的不足
- 功能比较单一,只有基础的四则运算和简单的扩展
- 不支持表达式直接输入,每次都要先选运算类型再输两个数,操作步骤多
- 没有历史记录功能,算完就忘了
- 错误处理不够全面,比如没有处理
KeyboardInterrupt
LLM生成程序的优点
- 功能非常全面,涵盖了日常计算的大部分需求
- 采用了面向对象的设计,代码结构清晰,便于扩展和维护
- 提供了丰富的人性化功能:历史记录、内存存储、ans引用上条结果、help帮助系统
- 错误处理做得比较全面,程序不容易崩溃
- 支持直接输入表达式(如
2+3*4),操作更自然
LLM生成程序的不足
- 代码量较大,对于初学者来说理解起来有一定门槛
- 基础运算部分使用了
eval()函数,虽然做了安全检查,但仍然是潜在风险点 - 需要导入
math、sys、datetime等模块,增加了依赖 - 类的方法比较多(10+个),追踪代码逻辑时需要来回跳转
总结
自写程序就像是搭了一个简单的积木房子,结构简单但够用;LLM生成的程序则像一栋功能齐全的大楼,什么都有,但建造和理解的复杂度也更高。在实际开发中,应该根据需求来选择——如果只是为了快速算个数,简单的就够了;如果要长期使用,功能丰富的版本更有价值。
3. 实验过程中遇到的问题和解决过程
-
问题1:用户输入非数字时程序崩溃
在最初的版本中,我直接用
float(input())获取用户输入,没有做任何校验。当我测试时不小心输入了字母,程序直接抛出了ValueError异常并退出。问题1解决方案:将输入逻辑封装到
get_number()函数中,使用try-except结构捕获ValueError。当用户输入非法内容时,打印提示信息并通过while True循环要求重新输入,直到输入正确为止。 -
问题2:除数为零导致程序出错
在除法、取模和整除运算中,如果用户输入第二个数为 0,程序会抛出
ZeroDivisionError。问题2解决方案:在执行这三种运算之前,先用
if num2 == 0进行判断。如果除数为零,打印错误提示并通过continue跳过本次运算,让用户重新输入。 -
问题3:LLM程序科学函数解析失败
在调试LLM生成的程序时,发现
sqrt(16)、sin(30)等科学函数无法正确计算,输出了奇怪的错误信息。问题3解决方案:检查后发现是
_parse_expression()函数中科学函数的字符串偏移量(offset)设置错误。比如sin(的长度是4,但我写成了6,导致截取参数时多截了两个字符。修正所有科学函数的偏移量后问题解决。 -
问题4:程序退出方式不够友好
第一版程序只能通过选择菜单中的"0"来退出,如果用户习惯输入
q或quit会感到困惑。问题4解决方案:在LLM生成版本中增加了多种退出方式,支持
quit、q、exit三种命令,同时还在主循环中处理了KeyboardInterrupt(Ctrl+C)和EOFError(管道输入结束),让用户可以用多种方式优雅退出。
其他(感悟、思考等)
通过这次计算器程序的设计实验,我对Python编程有了更实际的理解。
一开始写简易计算器的时候,觉得挺简单的,就是几个 if-elif 判断加一个 while 循环的事。但真正动手写才发现要注意的细节很多——用户可能会输入字母而不是数字、可能会让除数为零、可能会不知道怎么退出程序。这些"意外情况"的处理才是编程中最花时间的地方,也是让程序从"能用"变成"好用"的关键。
对比自写程序和LLM生成的程序,我最大的感受是:AI确实能快速生成功能丰富的代码,但代码不等于程序,程序不等于好用的产品。LLM生成的代码虽然功能多,但如果不理解每一部分的原理,出问题时都不知道从哪里查起。比如这次调试科学函数偏移量的问题,如果我只是复制粘贴而不去理解代码逻辑,可能就卡在那里了。
另外我还注意到,写代码时"过度设计"和"功能不足"是两个极端。我的自写程序有点太简单了,LLM的程序又有点太复杂了。真正好的设计应该是在满足需求的前提下保持简洁,这个度需要经验来把握。
这次实验也让我体会到了代码托管的重要性。把代码推到Gitee上,不仅能备份,以后回顾的时候也能看到自己的进步轨迹。
参考资料
- [《Python程序设计》课程教材]
- Python官方文档 - math模块
- Python官方文档 - 错误和异常
- 《Java程序设计与数据结构教程(第二版)》
- 《Java程序设计与数据结构教程(第二版)》学习指导
浙公网安备 33010602011771号