资讯动态

从能跑到能用:Python第二次作业的CSV数据处理实战解析

发布时间:2026/10/1 12:05:13 来源:尧图企业网站定制
第二次作业从能跑到能用的关键一跃每个学编程的人都绕不过这道坎第一次作业还在敲Hello World和简单的加减法第二次作业就开始要求你处理真实数据、动手设计程序结构了。说实话我当年做第二次作业的时候也一度对着需求文档发呆明明每个语法都见过组合到一起却不知道从哪里下手。今天就把我完成第二次作业的全过程拆开来讲——从怎么读懂题目背后的真实需求到程序结构怎么设计再到具体代码怎么写、坑怎么踩完整复盘一遍。这篇文章适合正在被作业折磨的初学者也适合想看看别人的第二次作业是怎么做的的朋友。核心就一句话第二次作业不是考你背了多少语法而是考你面对一个模糊问题时的拆解能力。1. 项目概述与需求拆解1.1 第二次作业到底在考什么先说说这份作业本身的定位。第一次作业通常是单知识点练习比如写一个函数计算两个数的和你只要把语法用对就能拿分。到了第二次作业题目就变成了读取一份学生成绩文件统计分析并输出报告这种综合性任务它考察的维度明显变了。我拿到的这份作业要求是这样的给定一份包含学生姓名、学号、三科成绩的CSV文件程序需要完成数据读取、缺失值处理、统计计算平均分、最高分、最低分、标准差最后按总分排序输出成绩报告。听起来不复杂但它同时涉及文件操作、数据结构、异常处理、排序算法、格式化输出五个知识点。这就是第二次作业的典型特征不再考单项技能而是考你把这些技能串起来的能力。所以第一步不是急着写代码而是想清楚老师出这个题目到底想让你练什么我的判断是三个词——健壮性、结构化、工程思维。健壮性是指你的程序遇到空值、脏数据不能直接崩溃结构化是指代码不能全部堆在main函数里工程思维是指你要考虑文件路径、编码、边界条件这些看不见的问题。1.2 需求解读别急着写代码很多同学拿到作业第一反应是打开编辑器开始敲我强烈不建议这么干。我拿到题目后先花了一个小时把需求拆成了下面几个问题数据文件长什么样有没有表头分隔符是逗号还是其他符号缺失值处理具体指什么是跳过、补零还是用平均值填充输出报告的格式有没有要求控制台打印还是写回文件排序遇到同分怎么处理学号是字符串还是数字这些细节题目里未必都写清楚了需要你自己做决定并在代码里明确体现。我的做法是先把假设写清楚表头默认存在、缺失成绩用该科目平均分填充、输出到控制台且按总分降序排序。这个需求假设清单特别重要既方便你自己检查答辩的时候也能说得清楚。我在实际做的时候发现很多同学作业跑不过、扣分多问题不在代码逻辑而在需求理解偏差——比如没处理表头、把学号当数字导致前导零丢失、空值行直接报错。这些都说明需求拆解这一关没过关。2. 整体设计与方案选型2.1 为什么用Python而不是Excel技术选型这一步二次作业的同学最容易忽视。有同学问这作业用Excel打开数据文件手工算不就行了为什么要写程序这里要明确一个概念作业的核心目的不是得到结果而是用代码处理问题。Excel能做到交互式操作但没法回答如果数据变成一万行怎么办如果每天更新一份新数据怎么办这些问题。程序化的价值在于可重复、可扩展、可自动化。我选择Python来写这份作业原因很简单第一Python标准库自带的csv模块直接支持CSV文件解析不用装第三方依赖第二语法接近伪代码更容易把精力放在逻辑本身而不是语法细节上第三在课程后续阶段同样的代码稍微改改就能处理JSON、Excel文件复用性高。如果你的课程指定了其他语言比如Java或C思路也是一样的只是文件处理的API不同。选型的底层逻辑永远不变优先选你最熟悉、能最快实现且老师认可的技术栈不要在工具上炫技。2.2 程序结构三个模块的分工设计程序结构的时候我参照了实际项目的分层思想。一个完整的成绩分析程序至少应该拆成三个模块数据读取模块负责读文件、解析CSV、把每一行转换成统一的数据结构。统计计算模块负责缺失值处理、平均分标准差计算、排序逻辑。输出展示模块负责格式化打印成绩报告或者写回新的文件。这样做的好处我做完之后体会特别深每一块都可以独立测试。数据读取出问题不用去翻排序的代码排序结果不对直接检查计算模块就行。对于作业来说这种结构还能让你在报告里明确写出每个函数承担什么职责这在评分标准里往往是加分项。我用一个Student的简单类或者字典来存放单条学生记录字段包括name、student_id、scores列表。数据结构的选择上字典比元组更直观比直接操作CSV二维数组更灵活。这里有个小建议写代码之前先把数据结构定下来数据结构定了整个程序的骨架就清晰了。3. 核心实现与关键代码3.1 数据读取编码和路径是第一个坑数据读取是整个程序的地基也是第一个坑最多的地方。我用的CSV文件长这样姓名,学号,语文,数学,英语 张三,2023001,85,92,78 李四,2023002,,88,90 王五,2023003,76,,82读取代码第一版我直接用了csv.reader跑起来发现中文全乱码了。原因是Windows下CSV文件默认可能是GBK编码Python默认按UTF-8读取。这个问题在作业里出现频率极高解决方案很简单import csv def read_data(file_path): students [] with open(file_path, r, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: students.append({ name: row[姓名], student_id: row[学号], scores: { 语文: float(row[语文]) if row[语文] else None, 数学: float(row[数学]) if row[数学] else None, 英语: float(row[英语]) if row[英语] else None } }) return students注意三处细节。第一我用了encodingutf-8-sig而不是utf-8这是为了绕开BOM头问题——有些Windows下生成的CSV文件会带一个不可见字符用utf-8读会导致第一列列名变成\ufeff姓名匹配不上。第二我用了csv.DictReader而不是csv.reader这样读取出来的每一行是字典直接用列名取值代码可读性高很多。第三成绩列我用列表推导式的条件表达式做了空值标记空字符串先转成None不在读取阶段就报错。路径问题同样值得单独说。很多同学把CSV文件和程序放在同一目录就以为万事大吉实际上如果你在终端里用绝对路径运行程序相对路径就可能失效。我的建议是使用Path(__file__).parent来定位脚本所在目录再拼接文件名这样无论从哪里运行都不会找不到文件from pathlib import Path BASE_DIR Path(__file__).resolve().parent file_path BASE_DIR / students.csv3.2 数据清洗空值和异常值处理读取完成之后数据大概率是脏的。这份作业里缺失值处理就是核心考察点之一。我处理的逻辑分两步先统计有哪些科目存在缺失再用该科目的平均值填充。为什么要用平均值而不是直接填零因为如果语文成绩平均值是80某个学生没参加考试填了0分他其他科目即使满分总分也会被严重拉低这不符合成绩报告的真实意义。平均值填充是业界处理数值型缺失数据最常用的简单方案之一。实现起来也不复杂def fill_missing(students): # 第一步统计各科目非空成绩的平均值 subjects [语文, 数学, 英语] avg_scores {} for subject in subjects: valid_scores [ s[scores][subject] for s in students if s[scores][subject] is not None ] avg_scores[subject] sum(valid_scores) / len(valid_scores) # 第二步用平均值填充缺失值 for student in students: for subject in subjects: if student[scores][subject] is None: student[scores][subject] round(avg_scores[subject], 2) return students这个模块的代码不多但逻辑上要防止两个问题。一是分母为零——如果某科目全体学生都没成绩len(valid_scores)等于0直接除零崩溃。真实作业不太会出现这种极端情况但我在代码里加了保护把平均分的默认值设为0。二是填充后的成绩保留几位小数我统一round到2位否则会出现79.9999999这种浮点计算误差影响后面排序和输出的美观。另外一个容易被忽略的问题是异常值。比如某行数据里成绩写成了abcfloat转换会直接抛ValueError。我的做法是在读取阶段包一层try-except遇到解析失败的单元格打印警告行号然后按缺失值处理。这样程序不会因为一行脏数据而整体崩溃也符合健壮性这个考察点。3.3 统计计算与排序输出最后一部分是计算的输出。每个学生的平均分是数学、语文、英语三科的平均总分是三者之和班级方差我用了统计学里的总体标准差公式除以n而不是样本标准差除以n-1。这两个公式的结果在n大的时候差别不大但作业里通常默认处理的是全体数据所以用总体标准差更合适。写的时候我特意在注释里标注了公式来源方便老师查看。排序部分我用sorted函数加上key参数指定排序依据def generate_report(students): for student in students: scores student[scores].values() student[total] round(sum(scores), 2) student[avg] round(sum(scores) / len(scores), 2) # 按总分降序同分时按姓名排序 students_sorted sorted( students, keylambda s: (-s[total], s[name]) ) print(成绩统计报告) print( * 60) for student in students_sorted: print( f{student[name]:6} f{student[student_id]} f语文:{student[scores][语文]:6.2f} f数学:{student[scores][数学]:6.2f} f英语:{student[scores][英语]:6.2f} f总分:{student[total]:7.2f} f平均:{student[avg]:6.2f} )这里的小技巧在keylambda s: (-s[total], s[name])。Python的sorted默认升序取负号就变成按总分降序当总分相同的时候Python会继续比较元组里的第二个元素也就是姓名升序。一次排序解决两个维度的规则简洁高效。格式化输出也值得花点心思。f{name:6}的意思是姓名左对齐占6个字符宽度f{score:6.2f}意思是数字右对齐、总宽度6、保留两位小数。对齐之后的报告在控制台里是整齐的表格状比默认字符串拼接美观太多。答辩演示的时候这一眼差距非常明显。如果你想把报告同时输出到文本文件在print外面套一层文件重定向即可这个扩展我后面单独说。4. 调试过程与常见问题4.1 我实际踩过的坑代码写完之后是调试阶段我把自己真实踩过的坑按出现顺序列一下每个都是作业场景里的高频错误。第一个坑是csv.DictReader的列名匹配失败。前面提过BOM问题第一次运行直接KeyError: 姓名。排查方法很简单把reader的fieldnames打出来看一眼发现多了一个不可见前缀字符。改成utf-8-sig后解决。第二个坑是空字符串转float报错。我没有在读取阶段做空值判断就直接float(row[语文])结果遇到空单元格直接ValueError。当时百思不得其解以为CSV里没有数据就不会读到那一行。后来打印原始行数据才知道CSV文件里的空格子读进来是空字符串不是None。这类问题用print调试法最直接——在出错位置前打印当前处理的行一看数据长什么样就知道问题出在哪。第三个坑是排序结果和预期不一致。我最初用sorted(students, keylambda s: s[total], reverseTrue)看起来没问题但总分相同时顺序不稳定。后来改成上面那个二元组写法解决。顺便说一句Python的sorted是稳定排序但稳定排序只保证原序列中相同元素的相对顺序不变不等于你可以依赖它实现二级排序二级排序还是要显式写清楚。第四个坑是浮点误差导致平均分显示不干净。比如数学平均分算出来是88.30000000000001打印出来又长又丑。虽然不影响正确性但我统一在计算和输出两个阶段都做了round处理同时保证内部计算精度和外部展示美观。4.2 常见问题速查表把作业中最常见的几个问题整理成一张表按出现频率排列方便你对照排查。症状根本原因解决方法中文乱码或列名KeyError文件编码不是UTF-8可能有BOM头open时用encodingutf-8-sig文件找不到FileNotFoundError相对路径依赖当前工作目录用Path(file).parent拼接绝对路径空单元格报ValueError空字符串被直接转float读取阶段先判断是否为None或空串排序结果不整齐只按总分排缺少多级排序规则keylambda s: (-s[total], s[name])平均分带很长的小数浮点数二进制表示误差计算和输出统一round(score, 2)除了表格里这些还有一个理念层面的问题必须说不要相信一步到位的代码。我每次改动完都会用一个最小数据集测试比如三个学生、其中一人缺考验证每个阶段的输出。宁可多写几个print把自己程序的中间状态看个明白也不要闷头写完最后一把梭。程序员的调试时间大部分都花在看中间状态上作业阶段养成这个习惯后面做项目受益无穷。5. 作业之外的思考这个程序还能怎么长作业提交完之后我额外做了一件小事在原程序的基础上给输出报告加了一个写文件的选项把控制台打印的同一份报告同时保存成report.txt。代码只多了一行重定向但程序就从给人看的工具变成了可留档的工具。细节虽小却是在培养一种意识程序的价值不只是跑一次过老师检查而是能被反复使用。顺着这个思路这个作业其实还可以扩展很多方向。比如把平均分填充改成按学号关联查找其他数据表或者把成绩数据画成简单的柱状图——用Python标准库做不了装个matplotlib就能搞定。再比如把程序改成命令行参数接收文件路径python analyze.py students.csv这样换一份数据完全不用改代码。这些扩展不是老师要求的但做完之后你对文件处理、参数解析、数据可视化这些知识的理解会比只做作业本身深得多。我个人在完成这份作业后最深的体会是第二次作业的价值不在分数而在它逼你完成了第一次从需求到代码的完整闭环。回想整个过程真正让我卡壳的不是哪个语法不会用而是面对一个问题我该先想什么再做什么的方法论。把这个流程走顺了后面不管是第三次第四次作业还是真实的业务需求你都会更有底气。如果你现在正卡在第二次作业某个坑里不妨先放下代码把我前面说的需求假设清单列出来从数据格式到输出格式都写清楚你会发现问题突然变简单了一大半。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑