零基础入门python02:写出第一个有用程序——家庭账单计算器
一、上一篇课后练习讲解
上一篇完成了 Python 3.11 安装、编辑器配置和第一行 print。练习的验收不是看屏幕上有没有文字,而是确认:终端能找到 python,文件能保存为 .py,从项目目录运行时退出码为 0。
可执行验收答案
在练习目录创建 check_env.py,不要依赖 VS Code 的运行按钮,直接从终端验证解释器和退出码:
import sys
print("executable:", sys.executable)
print("version:", sys.version.split()[0])
print("hello from lesson 01")
运行命令:
python check_env.py
python –c "import sys; assert sys.version_info[:2] == (3, 11); print('environment ok')"
预期看到 executable 指向当前项目解释器、版本为 3.11.x,第二条命令输出 environment ok。如果 python 找不到,Windows 可尝试 py -3.11 check_env.py;如果版本不是 3.11,先在 VS Code 选择正确解释器,再重新打开终端。脚本退出码为 0 才算通过,不能只看屏幕文字。
二、本篇要完成什么
我们要做一个家庭账单计算器。用户输入商品价格、数量和配送费,程序输出总价。通过这个小程序一次学会变量、字符串、整数、小数、输入转换、四则运算和格式化输出。

三、先看不带输入的版本
unit_price = 19.90
quantity = 2
delivery_fee = 5.00
total = unit_price * quantity + delivery_fee
print(f'应付金额:{total:.2f} 元')
变量是给数据起名字。unit_price 保存单价,quantity 保存数量,total 保存计算结果。这里用下划线命名,是因为 Python 社区约定变量名使用小写单词和下划线,读起来比 unitprice 更清楚。
四、让用户输入数据
unit_price = float(input('请输入商品单价:'))
quantity = int(input('请输入购买数量:'))
delivery_fee = float(input('请输入配送费:'))
total = unit_price * quantity + delivery_fee
print(f'应付金额:{total:.2f} 元')
input() 返回的永远是字符串,即使用户输入 19.9,得到的也是 '19.9'。所以金额要经过 float(),数量要经过 int()。如果把字符串直接和数字相乘,得到的会是字符串重复,而不是金额计算。
五、加入输入规则
unit_price = float(input('请输入商品单价:'))
quantity = int(input('请输入购买数量:'))
delivery_fee = float(input('请输入配送费:'))
if unit_price <= 0:
print('单价必须大于 0')
elif quantity <= 0:
print('数量必须大于 0')
else:
total = unit_price * quantity + delivery_fee
print(f'应付金额:{total:.2f} 元')
条件分支先阻止不合理数据,再计算结果。注意缩进:else 中的代码只有在前两个条件都不成立时才执行。
六、完整文件:bill_calculator.py
def calculate_total(unit_price: float, quantity: int, delivery_fee: float) –> float:
"""只负责计算金额,不负责读取输入,便于后续测试和复用。"""
if unit_price <= 0:
raise ValueError('单价必须大于 0')
if quantity <= 0:
raise ValueError('数量必须大于 0')
if delivery_fee < 0:
raise ValueError('配送费不能为负数')
return unit_price * quantity + delivery_fee
def main() –> None:
"""负责命令行交互,把用户输入转换后交给业务函数。"""
try:
unit_price = float(input('请输入商品单价:'))
quantity = int(input('请输入购买数量:'))
delivery_fee = float(input('请输入配送费:'))
total = calculate_total(unit_price, quantity, delivery_fee)
except ValueError as error:
print(f'输入有误:{error}')
return
print(f'应付金额:{total:.2f} 元')
if __name__ == '__main__':
main()
这里的 try 不是为了炫技,而是处理两类真实输入错误:用户输入 abc 导致类型转换失败,以及输入负数触发业务规则。把计算放在 calculate_total 中,可以在没有键盘输入的情况下直接测试。
七、运行结果
请输入商品单价:19.9
请输入购买数量:2
请输入配送费:5
应付金额:44.80 元
错误示例:
请输入商品单价:abc
输入有误:could not convert string to float: 'abc'
八、验收与练习
运行 python bill_calculator.py 完成一次成功和两次错误输入。课后练习:增加折扣参数,满 100 元打九折;下一篇会讲字符串、列表和元组,并把多个商品加入购物车。
实战补充:会员折扣边界实验
前面的计算器只有一条折扣规则,真实业务经常会增加会员等级。不要把新规则直接塞进 main;先为它写一个只接收参数的函数,再由命令行调用。
from decimal import Decimal
def apply_discount(subtotal: Decimal, level: str) –> Decimal:
rules = {'normal': (Decimal('100'), Decimal('0.90')),
'vip': (Decimal('80'), Decimal('0.85'))}
if level not in rules:
raise ValueError('会员等级只能是 normal 或 vip')
threshold, rate = rules[level]
if subtotal < threshold:
return subtotal
return (subtotal * rate).quantize(Decimal('0.01'))
def calculate_order(price: Decimal, quantity: int, fee: Decimal, level: str) –> dict:
if price <= 0 or quantity <= 0 or fee < 0:
raise ValueError('价格和数量必须为正,配送费不能为负')
subtotal = (price * quantity).quantize(Decimal('0.01'))
discounted = apply_discount(subtotal, level)
return {'subtotal': subtotal, 'discounted': discounted, 'total': discounted + fee}
逐个测试 99.99、100、79.99、80 四个门槛,能定位“边界前、边界上、边界后”三种结果。不要只测一个成功输入。
本篇项目验收记录
正常输入、满减、未知等级、负数和数量为零都要有预期结果。金额使用 Decimal,舍入发生在折扣后;如果产品要求每行先舍入,必须修改规则并补测试。
课后练习
为会员增加生日券,但生日券和折扣不能同时使用。先写冲突规则和测试,再改代码;下一篇会把一个订单扩展成多个商品。
本篇结束:完整模块文件
本节不是代码片段,而是本篇结束时该模块的完整版本。请先备份旧文件,再整体替换;替换后重新运行本篇命令和测试。阅读时重点看本篇新增的函数、事务边界和错误处理,未涉及的代码先不要自行删减。
main.py
subtotal=float(input('商品总价:'))
delivery=float(input('配送费:'))
coupon=float(input('优惠金额:'))
payable=subtotal+delivery–coupon
print(f'应付金额:{payable:.2f}元')


