Python 首批细教程 · 02A:用函数和模块做一个成绩统计 CLI
承上启下:在上一篇《Python 教程拆解索引 · 07:项目交付、工具链与表达》中,我们明确了 Git 版本管理、README 规范和 Docker 容器化等最小项目交付标准,建立了技术架构的工程化骨架。本篇作为实战细教程的首篇,我们将重新回到语言地基,利用纯粹的函数与模块设计,带您从零实现一个能够输入、解析并统计学生分数的命令行(CLI)成绩工具,实践核心的基础语法与异常防错设计。
对应原仓库:
Day01-15/06.函数和模块的使用.md、Day01-15/07.字符串和常用数据结构.md、Day01-15/11.文件和异常.md已提供可运行示例:
/tutorial-assets/python-100-days/02a-score-cli/(站点源码路径:blog-src/static/tutorial-assets/python-100-days/02a-score-cli/)
这一篇不讲“函数有什么好处”,直接做一个能用的小工具:从终端录入学生成绩,输出平均分、最高分、及格人数和等级分布。
最终效果
请输入:姓名,分数;输入 q 结束
> 张三,88
> 李四,92
> 王五,77
> 赵六,59
> q
总人数: 4
平均分: 79.00
最高分: 李四 92
及格人数: 3
等级分布: {'A': 1, 'B': 1, 'C': 1, 'D': 1}
Step 1:把输入解析拆成函数
直接使用示例目录里的 score_utils.py(站点源码路径:blog-src/static/tutorial-assets/python-100-days/02a-score-cli/score_utils.py):
def parse_record(text: str) -> tuple[str, int]:
name, score_text = text.split(",")
score = int(score_text.strip())
if not 0 <= score <= 100:
raise ValueError("分数必须在 0 到 100 之间")
return name.strip(), score
def get_level(score: int) -> str:
if score >= 90:
return "A"
if score >= 80:
return "B"
if score >= 60:
return "C"
return "D"
这里对应的就是原仓库里“用函数封装重复逻辑”的思想。parse_record 和 get_level 都是可复用模块,而不是散在主程序里的一堆判断。
Step 2:写统计函数
继续在 score_utils.py 里补:
def average(scores: list[int]) -> float:
return sum(scores) / len(scores) if scores else 0.0
def level_counter(scores: list[int]) -> dict[str, int]:
counter = {"A": 0, "B": 0, "C": 0, "D": 0}
for score in scores:
counter[get_level(score)] += 1
return counter
这一步就是把列表操作、循环和字典结合起来。
Step 3:写主程序
直接使用示例目录里的 main.py(站点源码路径:blog-src/static/tutorial-assets/python-100-days/02a-score-cli/main.py):
from score_utils import average, level_counter, parse_record
def main():
records: list[tuple[str, int]] = []
print("请输入:姓名,分数;输入 q 结束")
while True:
text = input("> ").strip()
if text.lower() == "q":
break
try:
records.append(parse_record(text))
except ValueError as err:
print(f"输入无效:{err}")
if not records:
print("没有有效数据")
return
scores = [score for _, score in records]
top_name, top_score = max(records, key=lambda item: item[1])
print()
print(f"总人数: {len(records)}")
print(f"平均分: {average(scores):.2f}")
print(f"最高分: {top_name} {top_score}")
print(f"及格人数: {sum(score >= 60 for score in scores)}")
print(f"等级分布: {level_counter(scores)}")
if __name__ == "__main__":
main()
运行:
cd blog-src/static/tutorial-assets/python-100-days/02a-score-cli
python main.py
这一篇到底学什么
不是“学会了几个语法点”,而是练下面这几个能力:
- 输入解析:把字符串变成结构化数据。
- 函数拆分:每个函数只做一件事。
- 模块复用:统计逻辑和交互逻辑分离。
- 异常处理:错误输入不让程序崩掉。
进阶任务
- 把数据保存到
scores.txt。 - 允许用户从文件读取成绩。
- 把
level_counter改成使用collections.Counter。
常见坑
split(",")后不做strip(),会带空格。- 分数不校验范围,
188也被算进去。 - 所有逻辑都塞进
main(),后面没法测试。
💡 下一篇预告:虽然我们的命令行工具已经能够顺利完成运行期的成绩统计,但一旦退出终端,所有的输入数据都将化为乌有。如何解决数据的持久化存储问题?在下一篇《Python 首批细教程 · 02B:把成绩工具升级成 JSON 成绩册》中,我们将引入 Python 内置的
json模块,将成绩工具升级为支持文件读写与持久化存储的 JSON 成绩册。