Python作业实战:从CSV读取到排序输出的完整避坑指南

发布时间:2026/10/14 20:27:52
Python作业实战:从CSV读取到排序输出的完整避坑指南 “Python作业1”这个词在课程任务列表里可能只是一个编号但对写的人而言它是从语法到程序的第一次完整跨越。我手头这个作业的需求并不复杂读取一份CSV格式的学生成绩表计算每个人的总分和平均分按总分降序排列最后把结果写成新的文件。真正动手之后才发现越是看起来简单的作业越能暴露各种平时注意不到的细节编码问题、文件路径问题、排序边界条件、CSV转义规则……这篇记录就把我从拿到“Python作业1”到最后提交的完整过程写下来包含代码设计、调试经过和我自己的避坑经验给正在做类似练习的读者一个可以对照的参考。1. 拿到需求先别急着敲代码拆解与选型1.1 先把含糊的一句话变成可执行的清单很多同学拿到作业描述后的第一反应是打开编辑器直接写我一开始也这样结果折腾半天交上去才发现漏掉了“平均分保留一位小数”这种附加要求。后来学乖了先动手拆需求把输入、处理、输出、边界条件都列成清单。比如我这份“Python作业1”原始描述就三行读取data/scores.csv字段包含姓名、语文、数学、英语计算每个学生的总成绩和平均成绩按总成绩降序排序后输出到output/result.csv。听起来很清晰但细想全是问题CSV文件里如果某一科成绩是空白是当作0分还是跳过两个学生总分相同谁排前面输出文件的第一行表头应该是什么平均分是保留整数还是小数这些都属于“需求没写清楚但程序必须自己给规则”的地方。我的做法是先把这些决策写进一个文档或注释里空白成绩按0分处理总分相同的情况下按姓名拼音排序表头固定为姓名、语文、数学、英语、总分、平均分、排名平均分保留一位小数。规则定好之后写代码就成了执行清单而不是边写边猜。这一步看着琐碎却是整个作业最关键的预处理。需求清单一旦列清楚后面所有函数的结构、参数、返回值都能顺着推出来返工频率会明显下降。1.2 选型脚本、函数还是类“Python作业1”的规模决定了技术选型不需要太复杂。我见过有人硬写一个Student类还加了两层继承最后老师点名提问时解释不清自己也被绕晕。对这个体量的作业我推荐按下面的表格做选择。方案复杂度可读性适合场景全脚本平铺最低中低逻辑长时容易看不清只有几十行、一次性执行函数封装中高按功能切分清晰大多数课程作业首选类和对象较高中初学者容易过度设计状态很多、需要复用或后续扩展我这个作业最终选择函数封装一个函数负责读取文件一个函数负责统计排序一个函数负责写结果主程序里依次调用。这样每个函数都能单独测试出了问题也容易定位。如果你对dataclass比较熟也可以定义一个简单的数据类来装学生的各科成绩但请记住一个原则作业阶段不需要任何“花哨”代码能被老师和同学一眼看懂比炫技重要得多。还有一个关于标准库的选择处理CSV时优先用csv模块而不是自己写字符串按逗号分割。原因很简单CSV文件中的字段可能包含逗号、引号或换行csv模块会自动处理转义手写split(,)极容易在真实数据上翻车。2. 环境准备和项目骨架把地基铺稳2.1 为什么建议用虚拟环境有人会觉得作业就一个文件还需要虚拟环境我的回答是需要而且要从第一次作业就养成习惯。因为你的机器上可能同时存在Python 3.8和Python 3.11不同项目依赖的版本也不同。虚拟环境能把当前项目的依赖隔离在单独目录里避免“开发机正常运行换台机器就跑不起来”的尴尬。实际操作非常简单python -m venv venv # Windows 下激活 venv\Scripts\activate # macOS / Linux 下激活 source venv/bin/activate这份作业只用了标准库不需要pip install任何第三方包但激活环境能保证Python解释器的版本是预期状态。有两点要特别提醒第一提交作业时千万别把venv文件夹一起交上去它体积大、包含大量你本机的路径信息老师不需要它。第二如果你在项目里用了第三方库记得额外生成一个requirements.txt让别人能快速复现环境。2.2 项目目录怎么摆才不乱如果是只有main.py一个文件的作业其实怎么放都行。但过两周回来再看一个孤零零的脚本会让人抓狂。我习惯用这样一个极简结构python_homework1/ ├── data/ │ └── scores.csv ├── output/ ├── main.py └── README.mddata目录放原始输入output目录放程序生成的result.csvmain.py是入口README.md写清楚运行方式和输入输出样例。有些老师会严格要求只提交一个.py文件遇到这种情况就不要硬拆模块了但要保证在一个文件内部用函数划分清晰并在文件开头用三引号注释写明作业编号、作者、日期和运行方法。我个人的一个习惯是先建目录再建空的__init__.py如果拆包的话最后再写代码。这样可以在写函数的同时直接按目录导入模块不用等到写完再调整路径。3. 核心功能实现从能跑到跑好3.1 读取CSV别让文件路径坑你一把读取文件是第一道坎也是最容易在运行时才暴露问题的部分。很多人会直接写open(data/scores.csv)然后从项目根目录运行这时候能跑通但如果你进入data目录再运行就会立刻遇到FileNotFoundError。原因很简单相对路径依赖的是“当前工作目录”而不是脚本所在目录。我采用的写法是用pathlib基于__file__定位这样无论从哪里执行都能找到文件from pathlib import Path BASE_DIR Path(__file__).resolve().parent DATA_FILE BASE_DIR / data / scores.csv读取时为了能按表头名字访问字段推荐csv.DictReaderimport csv def read_scores(file_path): students [] with open(file_path, moder, encodingutf-8-sig, newline) as f: reader csv.DictReader(f) for row in reader: students.append({ name: row[姓名].strip(), chinese: _to_score(row[语文]), math: _to_score(row[数学]), english: _to_score(row[英语]), }) return students这里有个很关键的细节encodingutf-8-sig。用普通utf-8读取时如果文件是带BOM的很多Excel另存出来的CSV都带第一列的字段名或第一个学生的姓名前面会多出一个不可见的\ufeff你会在输出里看到奇怪的乱码排查半天找不到原因。加-sig后缀就是告诉Python自动去掉BOM头属于一次配置、长期受益的做法。_to_score是我用来处理空白成绩的工具函数。这里体现出需求清单的价值如果规则是“空白按0分”那函数就可以统一处理def _to_score(value): text str(value or ).strip() return int(text) if text else 0这里没有直接用int(value)就是因为int()会抛异常。往往一行数据的某个空格就能让整个程序崩溃这种输入校验是作业评分时最容易挑刺的地方。3.2 统计、排序和并列排名读取完数据之后是统计逻辑。Python作业1的核心计算并不难但排序和排名有几个容易模糊的点。我写了一个纯函数输入学生列表输出带总分、平均分和排名的列表def compute_statistics(students): result [] for s in students: total s[chinese] s[math] s[english] avg round(total / 3, 1) result.append({ **s, total: total, avg: avg, }) # 先按总分降序再按姓名升序保证确定性 result.sort(keylambda x: (-x[total], x[name])) # 名次并列的总分给同名次 for i, item in enumerate(result): if i 0 and item[total] result[i - 1][total]: item[rank] result[i - 1][rank] else: item[rank] i 1 return result排序规则里-x[total]表示总分降序后面的x[name]处理总分相同的“平手”情况。如果不加后一个条件Python的排序仍然能完成但顺序是不稳定的可能每次运行结果不同。作业里如果出现了并列名次老师可能会随便抽查一个学生的排名你得能解释自己的规则。并列排名的处理方式有几种有的人用“1224”这种跳跃名次也有的人用“1223”这种连续名次。两者各有道理我选择了前者因为竞赛排名通常这么用。如果你不在乎并列完全可以用i1直接给每个人不同名次但一定要在注释里说明你选的是哪种规则。一个小经验不要在代码里“默认”一种规则却不对老师讲主动写清楚反而能成为项目的加分项。3.3 输出到CSV和控制台写结果时用csv.writer而不是手动拼字符串。手动拼,看着简单一旦某个名字或成绩里出现逗号、引号文件就坏了而且这种问题在Excel里才暴露很难排查。正确做法是def write_result(students, output_path): output_path.parent.mkdir(parentsTrue, exist_okTrue) with open(output_path, w, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow([姓名, 语文, 数学, 英语, 总分, 平均分, 排名]) for item in students: writer.writerow([ item[name], item[chinese], item[math], item[english], item[total], item[avg], item[rank], ])两个细节值得展开newline是为了避免在Windows上每行后面多一个空行写入时也用utf-8-sig是为了让你用Excel打开result.csv时中文不乱码。这两个坑都是“本地看着没问题一交换文件就出事”的类型踩过一次就永远记得。控制台的输出我习惯用格式字符串对齐那样检查结果比盯着一堆未格式化的数据舒服得多。比如def print_report(students): header f{姓名:8}{总分:6}{平均分:8}{排名:4} print(header) print(- * 30) for item in students: print(f{item[name]:8}{item[total]:6}{item[avg]:8}{item[rank]:4})加不加这个打印不影响作业的核心验收但演示给老师看的时候终端里整齐的输出比代码库本身更容易让人留下好印象。4. 调试实录我踩过的三个坑和常见问题速查4.1 坑一路径问题让我一度以为文件被删了第一次写完代码运行时报FileNotFoundError我的第一反应是检查data目录是否存在结果文件明明就在那里。后来在调试器里打印了当前工作目录才发现我在终端里进入的是项目根目录但很多教程里的代码都是直接open(scores.csv)这种写法只在上一条命令恰好把当前目录切到data时才能跑通。排查思路很简单打印Path.cwd()和__file__的绝对路径对比后发现二者不一致。解决方式是前文说的BASE_DIR方案。从那以后我写任何涉及文件的代码都默认用__file__定位这个习惯让我少踩了无数次坑。4.2 坑二CSV表头BOM带来的隐姓名字符第二版跑通后我发现输出的第一行第一个汉字前面多了一个类似\ufeff的符号。查了很多资料才知道UTF-8文件可能有BOMByte Order MarkExcel在保存CSV时默认会加上。普通文本编辑器看不到它但Python读取时就会把它当成字符的一部分。解决方法是读取和写入都用encodingutf-8-sig。我后来也发现如果没有-sig用DictReader读取时第一列的键名会变成\ufeff姓名你的代码如果写row[姓名]就会直接KeyError。这种情况特别容易让人误以为是表头写错实际就是编码的锅。4.3 坑三空字符串和空格让int()罢工还有一批数据里某一科成绩是空白的。直接调用int(row[语文])当值是 或时会抛ValueError。我最初的处理是int(row[语文] or 0)结果遇到空格字符串依然崩溃因为非空字符串在布尔判断里是True。最后用了一个专门的小函数把str(value or ).strip()作为统一入口空白就返回0否则转成整数。这也是我前面定义的_to_score的由来。这类问题不实际拿到脏数据根本想不到但程序的目标恰恰就是处理脏数据而不是只处理理想输入。4.4 常见问题速查表问题可能的原因解决方案FileNotFoundError使用了相对于当前工作目录的路径用Path(__file__).resolve().parent定位数据文件KeyError: 姓名CSV表头带BOM或列名有前后空格以utf-8-sig读取读取后strip()字段名中文输出乱码写入CSV用了utf-8Excel按ANSI打开写入用utf-8-sig每行CSV后面多空行打开文件时没设newlineopen(..., newline)int()转换报错成绩字段为空或含空格先strip()再判断空值转为0排序结果和预想不同没有处理总分相同的并列情况添加次要排序键如姓名运行后没有结果文件输出目录不存在用Path.mkdir(parentsTrue, exist_okTrue)在别的机器上运行失败依赖未打包、路径写死尽量用标准库路径改为相对__file__排查通用方法也值得多说一句遇到任何报错先看完整的Traceback不要只看最后一行。通常最后的错误信息指向的是“表现”往上翻几行才是“原因”。另外我习惯在关键函数里临时加print输出中间结果定位到具体哪一步出错后就删掉这比一脸懵地读代码高效得多。5. 让作业从“能跑”变成“好作业”测试、注释与文档5.1 给核心逻辑补几个单元测试很多初学者觉得测试是工作里的事作业随便跑一次能出结果就行。但“能跑”和“正确”是两个概念。我给这份作业补了三个简单测试用的是Python自带的unittest不需要额外装包。import unittest from main import compute_statistics class TestCompute(unittest.TestCase): def test_average(self): students [{name: A, chinese: 90, math: 80, english: 70}] result compute_statistics(students) self.assertEqual(result[0][total], 240) self.assertEqual(result[0][avg], 80.0) def test_sort_desc(self): students [ {name: A, chinese: 90, math: 80, english: 70}, {name: B, chinese: 100, math: 100, english: 100}, ] result compute_statistics(students) self.assertEqual(result[0][name], B) def test_tie_rank(self): students [ {name: A, chinese: 90, math: 80, english: 70}, {name: B, chinese: 90, math: 80, english: 70}, ] result compute_statistics(students) self.assertEqual(result[0][rank], 1) self.assertEqual(result[1][rank], 1) if __name__ __main__: unittest.main()测试的意义不只是求个心理安慰。我把排序规则改成“同分按姓名升序”的时候如果没有测试只能靠肉眼扫描输出文件有了测试一键就能确认规则没有破坏。这种反馈速度在后续改代码时太重要了。如果你还没用过unittest建议从这种小函数开始尝试比学一堆测试框架更实用。5.2 注释写“为什么”而不是“是什么”老师检查作业时代码风格有时比功能更重要。我见过很多注释长这样# 循环遍历学生列表 for s in students:这种注释完全没有信息量因为看代码本身就知道它在遍历。真正有价值的注释是解释“为什么”# 空白成绩统一按0分处理避免 int() 在空格字符串上崩溃 return int(text) if text else 0另一个提升可读性的小技巧是给函数加类型标注。比如compute_statistics(students: list[dict]) - list[dict]即使不运行读者也知道它接收什么、返回什么。配合一句话docstring整个项目的可读性会高一个档次。不要小看这份“表面功夫”。代码风格整洁本身就是作业评分点之一尤其那些没法自动化验收的主观题。给老师一个容易阅读的项目比给一个功能正确但一团乱麻的脚本拿到的分数往往截然不同。5.3 README给自己留后路也给别人留入口我最后给“Python作业1”补了一个简短的README内容只有三段运行方法、输入格式、输出样例。# Python作业1 运行方式 python main.py 输入 data/scores.csv表头为 姓名,语文,数学,英语 输出 output/result.csv包含 姓名,语文,数学,英语,总分,平均分,排名这个东西不花十分钟但一周后你再回看这份作业时不用重新读代码就能回忆起整个项目。如果这份作业是继续往后迭代的“作业1”一份清晰的README更是后续所有作业的基础。个人经验是每次交任何项目都写README哪怕只有三行长期积累下来你会发现自己对“一份完整的交付物”越来越有感觉。一些真正的个人收尾如果让我给正在写“Python作业1”之类练习的人一个建议我会说第一次先别追求完美先把整个流程从头到尾跑通哪怕代码又乱又长只要正确你就有了一个可以继续打磨的底子。我最开始在排序和编码上耗费了大量时间后来才发现这些都属于“有标准答案”的问题查书、查文档、看报错都比自己硬扛要快。最后再分享一个小技巧把需求清单直接写成注释框架放在文件开头每实现一个功能就勾掉一条。我做完“Python作业1”回头一对比发现绝大多数返工都来自没写下来的隐性规则——缺考算零分、并列怎么排名、文件用什么编码。把这些东西在动手前就固化成本文这样的文字你的编程作业会顺利很多而且这些东西也会悄悄变成将来做真实项目时的好习惯。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询