【Python 习题笔记 02】文件读写与列表处理:气温统计、成绩分档、函数封装与排序

发布时间:2026/10/23 4:51:33
【Python 习题笔记 02】文件读写与列表处理:气温统计、成绩分档、函数封装与排序 本系列完整合集Python 习题笔记合集程序结构、文件处理与函数封装完整合集原分篇继续公开保留可按需查阅。这一篇对应《Python 程序设计》习题集里连续五页的动手题从第 6 页一直排到第 10 页。把它们放在一起看会发现题目表面上各不相同——有气温、有成绩、有排序——但骨架其实只有一条线先把文件读成字符串再把字符串切成列表把列表里的字符串转成整数然后用循环把整数加工成结果最后写回文件或打印出来。所以我不按题号顺序抄一遍而是按知识点重新归类。一共八节前三节讲「文件 → 列表 → 数字」这条数据流水线第四节讲同一道题的第二种写法第五节讲字符串方法第六节讲怎么把三个需求拆成三个函数第七、八节讲两道算法题的套路。本系列导航01 程序结构与算法条件判断、循环与入门经典题02 文件读写与列表处理气温统计、成绩分档、函数封装与排序本篇03 函数封装与文件处理字符统计、素数判断、成绩汇总与有序表插入一、把文件读成列表open、readline、strip、split 四件套题目temp.txt的第一行是这一周每天的最高气温第二行是每天的最低气温数字之间用英文逗号分隔。请把这两行分别读进两个列表为后面的统计做准备。思路文件里的所有内容本质上都是字符串所以要分四步走。第一步open打开文件拿到句柄第二步用readline()读一整行第三步用strip()去掉行末的换行符第四步用split(,)按逗号切成列表。这四步的顺序不能颠倒——先strip再split否则最后一个元素会带着换行符转整数时会报错。读完之后立刻close()这是习题集里反复强调的一步。参考代码flnametemp.txtfopen(flname)ht(f.readline()).strip()L1list(ht.split(,))lt(f.readline()).strip()L2list(lt.split(,))f.close()foriinrange(len(L1)):L1[i]int(L1[i])L2[i]int(L2[i])print(最高气温列表:,L1)print(最低气温列表:,L2)上面这段for循环是把字符串列表原地转成整数列表等价于L1 [int(x) for x in L1]。两种写法都要会考试里看到哪种都不该慌。易错点split()括号里什么都不写是按空白字符分割能顺便吞掉连续空格而split(,)只按逗号分两者不能混用。另外L1 list(ht.split(,))里的list()其实可以省略split返回的已经是列表。一句话总结读文件的固定套路是open → readline → strip → split → close顺序错了就会把换行符一起吃进数据里。二、打擂台法求极值顺便算日均气温题目在上一题的基础上求出这一周的最高气温和最低气温分别出现在第几天再求出全周的平均气温各天平均温度的平均值取整数。假设入春标准是连续 5 天日均气温超过 10℃根据这一周的数据判断是否已经入春。思路求极值最经典的写法是打擂台——先把第一个元素当作擂主然后让后面每个元素上来挑战谁更极端谁就当新擂主同时记下它的下标。这里要同时记两件事极值本身和它所在的位置。日均气温则是最高温和最低温的算术平均所以要把两个列表对应位置相加再除以 2。参考代码maxValL1[0]maxDay0minValL2[0]minDay0foriinrange(len(L1)):ifL1[i]maxVal:maxValL1[i]maxDayiifL2[i]minVal:minValL2[i]minDayiprint(这周第str(maxDay1)天最热, 最高str(maxVal)摄氏度)print(这周第str(minDay1)天最冷, 最低str(minVal)摄氏度)L3[]foriinrange(len(L1)):L3.append(int((L1[i]L2[i])/2))total0k0foriinrange(len(L3)):totaltotalL3[i]ifL3[i]10:k1else:k0avgint(total/len(L3))print(周平均气温为:,avg)ifk5:print(上海这周已入春)else:print(上海这周末入春)这里我做了两处修正。第一扫描件里把累加变量写成了sum但sum是 Python 的内置函数名覆盖它会带来隐患所以改成total。第二扫描件里记擂主下标的初值写得含混按语义应当是下标0因为擂主就是列表第一个元素。易错点int((L1[i] L2[i]) / 2)是向零截断气温都是正数时没问题但如果出现负数int(-2.5)得到-2而-2.5 // 1得到-3两者不同。还有更隐蔽的一点这个k在遇到低于 10℃ 的那天会被清零所以它统计的只是最后一段连续高温而不是全周最长的一段。要判断「是否出现过连续 5 天」正确的做法是额外维护一个最大值best max(best, k)再拿best去比较。统计目标循环里要维护的量关键动作极值及其位置擂主值 擂主下标先设初值再逐个挑战末尾连续段长度一个计数器k命中加一不命中清零全周最长连续段计数器k 历史最大值清零前先更新历史最大值一句话总结打擂台法的核心是「先立擂主再逐个挑战」而连续段问题里计数器清零前要不要留一份历史最大值是区分对错的关键。三、从 score1.txt 到 score2.txt加权计算与分档统计题目当前目录下score1.txt存放着某班学生的计算机课成绩共学号、平时成绩、期末成绩三列第一行是表头。请按平时成绩占 40%、期末成绩占 60% 的比例计算总评成绩并取整把学号和总评成绩两列写入score2.txt同时在屏幕上输出学生总人数、各分数档人数90 以上、8089、7079、60~69、60 分以下和班级总平均分取整。思路这是一道「一进一出加统计」的综合题。输入侧用readline逐行推进输出侧一边写文件一边做统计。五个档位用一个长度为 5 的计数列表c来装判断时必须从高分档往低分档写elif这样每个分数只会落进第一个满足条件的分支。注意先跳过表头再做循环。参考代码fopen(score1.txt)af.readline()line(f.readline()).strip()f2open(score2.txt,w)f2.write(学号 总评成绩\n)c[0,0,0,0,0]count0total0whilelen(line)!0:L1line.split()f2.write(L1[0] )f_scoreint(int(L1[1])*0.4int(L1[2])*0.6)iff_score90:c[0]1eliff_score80:c[1]1eliff_score70:c[2]1eliff_score60:c[3]1else:c[4]1count1totalf_score f2.write(str(f_score)\n)line(f.readline()).strip()f.close()f2.close()avg_scoreint(total/count)print(学生总人数为 %d, 90 以上 %d 人, 80-89 间 %d 人, 70-79 间 %d 人, 60-69 间 %d 人, 60 分以下 %d 人, 班级总平均分为 %d 分%(count,c[0],c[1],c[2],c[3],c[4],avg_score))易错点扫描件里最高档写的是if 90 f_score 100这把恰好 90 分的人挤进了 80~89 档。题目说的是「90 以上」按中文习惯应当包含 90所以我改成了f_score 90——分档题的边界一定要看清是「以上」还是「超过」。另外while len(line) ! 0的终止条件依赖readline读到文件尾时返回空字符串所以循环体内必须先处理当前行、再读下一行顺序反了会漏掉最后一行。第三取整要放在算完平均分之后再取如果先把每个人的总评取整再平均结果会不一样。一句话总结分档统计的模板是「一个计数列表 从高到低排的 elif」而while len(line) ! 0这种循环要特别小心「先处理再读下一行」的顺序。四、同一道题的第二种写法readlines 加列表套列表题目还是上一题换一种文件推进方式重做一遍。思路readlines()一次性把整个文件读成一个字符串列表配合del a[0]删掉表头之后就能用for循环遍历。这种写法把「读入」和「加工」拆成了两步先扫一遍算出每个学生的总评把[学号, 总评]这样的小列表塞进大列表再扫第二遍做分档和写文件。代码结构更清楚代价是多占一份内存。参考代码fopen(score1.txt)af.readlines()dela[0]L3[]forlineina:lineline.strip()L1line.split()f_scoreint(int(L1[1])*0.4int(L1[2])*0.6)L3.append([L1[0],f_score])f.close()c[0,0,0,0,0]count0total0f2open(score2.txt,w)f2.write(学号 总评成绩\n)forL2inL3:ifL2[1]90:c[0]1elifL2[1]80:c[1]1elifL2[1]70:c[2]1elifL2[1]60:c[3]1else:c[4]1count1totalL2[1]f2.write(L2[0] str(L2[1])\n)f2.close()avg_scoreint(total/count)print(学生总人数为 %d, 90 以上 %d 人, 80-89 间 %d 人, 70-79 间 %d 人, 60-69 间 %d 人, 60 分以下 %d 人, 班级总平均分为 %d 分%(count,c[0],c[1],c[2],c[3],c[4],avg_score))易错点del a[0]是在原列表上删除第一个元素不是返回一个去掉表头的新列表所以不能写成a del a[0]。for L2 in L3里的L2是一个两元素列表读成绩要用L2[1]写成L2[1]和L2[0]千万别混。还有一个细节f2.write的参数只能是字符串拼接L2[1]之前必须str()转换这是初学者最高频的TypeError来源。对比项readline 加 whilereadlines 加 for内存占用只留一行占用小整文件进内存代码结构读与算揉在一个循环里可先加工再统计分层清楚循环终止靠空字符串判断靠列表遍历自动结束跳表头先额外读一行丢掉del a[0]适用场景文件很大时需要反复扫描同一批数据时一句话总结readline是「边走边算」readlines是「先全部拿进来再算」两种写法没有对错区别只在内存和结构的取舍。五、字符串方法整文件复制并把小写字母变大写题目当前目录下有sample12.txt内容含小写字母和大写字母。请把该文件复制成sample12_copy.txt并把原文件中的小写字母全部转换为大写字母其余格式均不变。思路readlines()读到的每一行自带换行符这正是「格式不变」的关键。只需要对每一行调用upper()再把结果原样写出去换行符就会自动保留行数和空行都不会变。参考代码fopen(sample12.txt)L1f.readlines()f2open(sample12_copy.txt,w)forlineinL1:f2.write(line.upper())f.close()f2.close()易错点upper()返回一个新字符串原字符串不会改变所以必须写成f2.write(line.upper())单写一行line.upper()是没有效果的。第二如果出于习惯写了line.strip().upper()换行符就被吃掉了复制出来的文件会变成一大坨——题目特意强调「其余格式均不变」就是在提醒不要strip。第三写模式用w如果用w或忘了打开第二个文件句柄会直接覆盖原文件。一句话总结字符串方法都返回新对象、不修改原对象而readlines保留换行符这件事正好是「格式不变」需求的实现手段。六、用函数把一份数据拆成三个问题题目当前目录下有class_score.txt存放某班学生的学号、数学课成绩第 2 列和语文课成绩第 3 列。要求(1) 分别求数学和语文的平均分保留 1 位小数(2) 找出两门课都不及格都低于 60的学生输出学号和各科成绩(3) 找出两门课平均分在 90 分以上的学生输出学号和各科成绩。要求用三个函数分别实现。思路三个需求互不依赖唯一共享的东西是那份数据所以最自然的切法是——在外面把文件读成一个列表把列表当参数传给三个函数。这就是函数封装最朴素的价值同一份数据被反复使用而每次使用的逻辑互不干扰。三个函数各司其职主程序只剩下读文件和三次调用。参考代码defoutput_avg(L):sum10sum20forlineinL:L1line.strip().split()sum1int(L1[1])sum2int(L1[2])countlen(L)avg1round(sum1/count,1)avg2round(sum2/count,1)print(这个班的数学平均分为: %4.1f, 语文平均分为: %4.1f%(avg1,avg2))defoutput_notpass(L):print(两门课均不及格的学生学号及数学 语文成绩为:)forlineinL:L1line.strip().split()ifint(L1[1])60andint(L1[2])60:print(line)defoutput_good(L):print(两门课平均分在 90 分以上的学生学号及数学 语文成绩为:)forlineinL:L1line.strip().split()f_scoreround((int(L1[1])int(L1[2]))/2)iff_score90:print(line)fopen(class_score.txt)Lf.readlines()delL[0]output_avg(L)output_notpass(L)output_good(L)f.close()易错点第一列号和下标差 1。学号是第 1 列对应L1[0]数学是第 2 列对应L1[1]语文是第 3 列对应L1[2]写代码时按题目里的「第几列」直接当索引用就会全体错位。第二del L[0]删表头必须放在readlines之后、调用函数之前否则平均分的分母会把表头也算进去。第三「都不及格」要用and连接而不是or用or会把只挂一门的学生也捞出来。第四round是银行家舍入round(2.675, 2)未必得到2.68涉及精确小数时更稳妥的做法是格式化输出而不是round。需求遍历时判断输出内容求两科平均分不需要判断全部累加两个百分数保留 1 位小数两门都不及格int(数学) 60 and int(语文) 60原始整行两门平均分 90 分以上round((数学 语文) / 2) 90原始整行一句话总结函数封装的切分依据是「共享数据 独立逻辑」——数据在外面读好当参数传进去函数内部只关心自己那一个问题。七、排序算法选择排序与冒泡排序题目编程实现从键盘接收若干个整数直接输入回车表示结束用冒泡法或选择法从小到大排序并把排序结果输出到屏幕。思路两道题其实共用同一个主程序差别只在排序函数内部。选择排序是定位置找元素外层循环确定第 i 个位置内层循环在 i 之后找最小值找到了就交换。冒泡排序是相邻比较、逐轮上浮内层从后往前两两比较把小的往前换。两者都是双重循环、都是原地交换。参考代码defselection_sort(List):numlen(List)foriinrange(0,num-1):forjinrange(i1,num):ifList[j]List[i]:List[i],List[j]List[j],List[i]returnListdefbubble_sort(List):foriinrange(0,len(List)-1):forjinrange(len(List)-1,i,-1):ifList[j-1]List[j]:List[j-1],List[j]List[j],List[j-1]returnList L1[]num_strinput(请输入一个需排序的整数: )whilelen(num_str)!0:L1.append(int(num_str))num_strinput(请输入一个需排序的整数: )print(排序后结果:,bubble_sort(L1))这里必须做两处修正都是扫描件里的明显笔误。第一扫描件把选择排序的函数名也写成了bubble两个函数同名会互相覆盖所以改名为selection_sort。第二扫描件里选择排序的内层判断写的是if List[j] List[i]这个方向得到的是从大到小与题目要求的从小到大相反所以把大于号改成了小于号。易错点input()返回的永远是字符串直接回车得到的是空字符串所以判空要写len(num_str) ! 0或num_str ! 而不能在判空之前就int(num_str)否则用户一按回车程序立刻抛ValueError。第二交换两个元素用List[i], List[j] List[j], List[i]就够了不需要中间变量但一定要写在同一行里。第三外层循环只到num - 1因为最后一个元素不需要再排内层range(len(List) - 1, i, -1)的第三个参数-1表示倒着走漏了它就是死循环或者白跑。第四函数里返回的是List调用时要print它的返回值直接print(bubble_sort(L1))比先调用再打印更省事。对比项选择排序冒泡排序内层比较对象List[i]与List[j]List[j - 1]与List[j]内层扫描方向从 i1 往后从末尾倒着往前每轮结束的效果第 i 个位置确定为最小值第 i 个位置确定为最小值交换次数较少较多时间复杂度O(n 平方)O(n 平方)一句话总结选择排序是「定位置找元素」冒泡排序是「相邻比较、逐轮上浮」两者的循环边界和比较符号是考试最容易设坑的地方。八、打擂台法找最高分和最低分题目当前目录下有score2.txt存放着某班学生的计算机课成绩共学号、总评成绩两列。请查找最高分和最低分的学生并在屏幕上显示其学号和成绩。思路扫描件这一题只印到了开头几行读完del a[0]、L2[]、L3[]就换页了后面的内容没有给出。所以下面按打擂台法补一个等价的最小实现先把文件读成「学号 整数成绩」的二元列表再让第一个学生当擂主逐个挑战。参考代码fopen(score2.txt)af.readlines()dela[0]L2[]forlineina:lineline.strip()ifline:continueL1line.split()L2.append([L1[0],int(L1[1])])f.close()maxStuL2[0]minStuL2[0]foriteminL2:ifitem[1]maxStu[1]:maxStuitemifitem[1]minStu[1]:minStuitemprint(最高分学生:,maxStu[0],maxStu[1])print(最低分学生:,minStu[0],minStu[1])易错点第一擂主初值不能写成0或者空列表。如果班里所有人都是 0 分用 0 当最高分初值就找不到人如果出现负分用 0 当最低分初值也一样会错。正确的做法永远是拿数据里真实存在的第一个元素当擂主。第二readlines的结果里可能夹着空行line.split()遇到空行会返回空列表紧接着的L1[1]就会抛IndexError所以要先continue跳过。第三这里把整个item列表赋给maxStu之后对maxStu只是读不写所以不会污染原数据但如果后续要修改它就必须用切片item[:]做浅拷贝。一句话总结打擂台法求极值初值一定是数据中的真实元素而列表套列表的写法能让「学号 成绩」这两个信息一起被记住。本篇最需要记住的几句话读文件的固定套路是open → readline → strip → split → close顺序错了就会把换行符一起吃进数据里。split出来的是字符串列表参与比较和求和前必须先int转换这是所有统计题的第一步。打擂台法的初值必须是数据中真实存在的第一个元素用0当擂主是隐藏最深的错误。连续段计数器的清零逻辑决定了答案对不对只维护一个k只能得到末尾那一段要得到全周最长的一段必须再维护一个历史最大值。分档统计用「一个计数列表 从高到低排的elif」边界词是「以上」还是「超过」要看清。readline与readlines是两种风格而不是两种对错前者边读边算省内存后者先读后算结构清楚。字符串方法都返回新对象line.upper()不写进write就等于没做。选择题排序还是冒泡排序看内层比较的是List[i]还是List[j-1]input()判空必须在int()之前。本篇易错点与记忆提示sum不要当变量名它是内置函数覆盖之后同一作用域里再用sum(L)就会报错。习题集里多处直接拿sum累加写成total更稳妥。int是向零截断不是四舍五入int(89.9)得到89如果要四舍五入必须先round。总评成绩写的是「取整」两种理解会差一分。int((最高 最低) / 2)和(最高 最低) // 2在负数上不等价气温题出现负值时就会露馅。while len(line) ! 0循环里先处理当前行、再读下一行顺序反了会漏掉最后一行或者直接跳过第一行。del a[0]是原地删除不是表达式不能写在赋值号右边。列号与下标差 1题目说第 2 列代码里就是L1[1]这是成绩统计题最常见的错位来源。w模式会清空原文件往同一个路径写之前想清楚尤其别把输入文件和输出文件写成同一个名字。扫描件里的排序函数名有笔误选择排序也叫bubble两个同名函数后定义的会覆盖先定义的改名前一定要核对。round采用银行家舍入round(2.5)得到2而不是3对成绩取整有影响时要改用格式化输出。【此处有图一条从temp.txt与score1.txt出发的数据流水线示意依次经过open/readline、strip/split、int 转换、循环统计、write 回盘五个方框并在split与int之间标注易错点「字符串列表必须先转整数」】

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询