Python基础进阶:函数、文件操作与异常处理实战指南

发布时间:2026/10/18 4:57:26
Python基础进阶:函数、文件操作与异常处理实战指南 1. 从入门到写工具第三阶段该补什么拿到“Python基础3”这个题目我第一反应是这大概率不是指“Python 3.x版本”而是一个系列课程的第三阶段。很多自学Python的同学学完变量、循环、列表这些基础语法之后会陷入一个很尴尬的区间——能看懂代码但让自己写个小工具、处理个文件、做个批量操作就无从下手。如果你正卡在这个位置这篇内容就是写给你看的。“基础3”这个阶段核心任务是从“认识Python”过渡到“用Python解决问题”。我见过太多初学者跳过这个阶段直接啃爬虫和数据分析结果遇到一点异常就懵了连错误日志都不会读。第三阶段该补的核心能力在我看来是四大块函数与作用域、模块与文件操作、异常处理、面向对象思想。把这四块打扎实你才真正具备“用代码干脏活累活”的基础体力。这篇文章会从实际需求出发按一个完整小项目的思路把这四个知识点串起来讲。每个知识点都带实操代码和常见的坑尽量让你看完能直接在自己的电脑上跟着跑一遍。2. 函数与作用域把代码“封装”起来2.1 你不该再用一段代码走天下了很多人在前两个阶段写代码习惯是这样的从上到下把逻辑写完程序运行完就结束。一旦要重复做类似的事情就复制粘贴。比如要计算三个不同班级的平均分就把同样的循环代码复制三遍改改变量名。这种做法在小程序里勉强能跑但只要程序超过200行维护成本会迅速失控。函数就是来解决这个问题的。我经常跟身边的人打比方函数就是一个“可重复调用的加工车间”你给它原料参数它按固定的流程加工然后给你产出返回值。加工流程一旦定下来你就可以反复使用而且将来要改加工规则只需要改车间内部不用到外面到处找代码改。看个最简单的例子计算平均分def calc_avg(scores): 计算一组分数的平均值保留1位小数 if not scores: return 0.0 total sum(scores) avg total / len(scores) return round(avg, 1)这个函数接收一个分数列表空列表会返回0.0避免报错非空则返回平均值。定义好之后你需要处理哪组数据就调用一次result calc_avg([88, 92, 75])就这么简单。2.2 参数传递和作用域大多数人搞混的细节初学函数时最容易踩的坑是参数类型和值传递的问题。Python里“传参到底是传值还是传引用”这个经典问题经常把新手绕晕。我用一句话帮你理清传的对象是可变类型列表、字典、集合时函数内部修改会影响外面的原始数据传不可变类型数字、字符串、元组时无论怎么改外面都不变。看这个例子你就明白了def add_score(scores, new_score): scores.append(new_score) my_scores [60, 70] add_score(my_scores, 80) print(my_scores) # 输出 [60, 70, 80]外部数据被修改了这是很多人在写代码时没预料到的“副作用”。有时候你正是需要这种修改比如批量更新配置但如果你只是想“计算一下”却在函数里不小心改了外部数据那就很难排查了。我的习惯是函数里尽量不要修改传入的可变对象除非函数的功能本身就是为了修改它。需要新结果时创建新对象返回这样最省心。作用域则是另一个重点。Python查找变量遵循LEGB规则局部Local→ 闭包Enclosing→ 全局Global→ 内置Built-in。简单来说函数内部优先使用局部变量没有才会往外层找。如果你在函数里写了x 10它就创建了一个局部变量跟外面的全局变量x没有任何关系。想在函数里修改全局变量必须用global声明但说实话日常开发里我几乎不用它。靠函数参数传递值比修改全局变量干净10倍。2.3 匿名函数和常用内置函数搭配Python里有一种轻量级的函数定义方式——lambda表达式。它适合那种逻辑很简单、只用一次的场景。比如按分数排序students [(小明, 88), (小红, 92), (小刚, 75)] sorted_students sorted(students, keylambda item: item[1], reverseTrue) # 输出 [(小红, 92), (小明, 88), (小刚, 75)]lambda item: item[1]的意思是“取元组的第二个元素作为排序依据”。这种写法看起来很高级实际就是一个匿名函数。配合map、filter、sorted这些内置函数处理数据时非常高效。但有一点要提醒lambda只适合一句表达式逻辑超过一行老老实实用def定义普通函数可读性好得多。3. 模块与文件操作让你的程序走出“单文件”3.1 为什么需要模块化和包结构写到第三个阶段你的程序开始变长单一.py文件里几百行代码挤在一起找问题就像在一间堆满杂物的房间里找钥匙。模块化的意义就是把不同功能的代码拆到不同文件里各司其职。Python原生的模块系统用起来很直观你新建一个util.py里面写几个函数然后在另一个文件里import util就能用util.函数名()调用了。但要注意一个目录要被Python认作包必须在里面放一个__init__.py文件。这个文件可以是空的它的存在告诉解释器“这个目录是一个包”。实际项目里通常这样组织project/ ├── main.py # 入口文件 ├── utils/ │ ├── __init__.py │ ├── file_helper.py # 文件读写相关的函数 │ └── stats.py # 统计相关的函数 └── config.py # 配置文件从main.py里导入时这样写from utils.file_helper import read_file from utils import stats3.2 文件读写编码问题是第一道坎文件操作是这个阶段必须练熟的技能最常见的应用就是批量处理文本、导出报表、处理日志。日常90%的场景用的都是open()函数但很多人第一次用就会栽在编码上。Python的open()默认编码方式跟操作系统相关在Windows中文系统上默认可能是gbk而文件是UTF-8编码时直接读会报UnicodeDecodeError。规避方法很简单——显式指定编码with open(data.txt, r, encodingutf-8) as f: content f.read()这里用with语句很关键。with会在代码块执行完之后自动关闭文件不用你手动调用f.close()。忘记关闭文件数据可能没有完全写进去用with就永远不会有这种烦恼。如果需要逐行处理大文件不要用read()一次性读入那会把内存撑爆。用迭代方式with open(big_log.txt, r, encodingutf-8) as f: for line in f: # 对每一行进行处理 process(line)写文件时同理with open(result.csv, w, encodingutf-8-sig, newline) as f: f.write(姓名,分数\n) f.write(小明,88\n)注意编码用的是utf-8-sig而不是utf-8。这个细节很多人不知道utf-8-sig会写入一个BOM头Excel打开导出的CSV文件时才不会乱码。如果你生成的CSV是给Excel用的这个坑一定要避开。3.3 路径处理为什么你该用pathlib而不是拼字符串早期学Python时很多人都是直接拼接路径字符串path data/ filename。这种做法在Windows下会遇到一个经典问题——路径分隔符不一样。Windows用反斜杠\Linux和macOS用正斜杠/。你写data/ filename在Windows上可能就变成data\file.txt表面看着没问题但一旦路径需要跨平台传输或嵌入URL就会出乱子。从Python 3.4开始官方推荐的pathlib模块把路径当对象来处理。这是我非常推荐你尽早习惯的库from pathlib import Path data_dir Path(data) result_file data_dir / result.txt # 用 / 连接路径 print(result_file.exists()) # 检查文件是否存在Path对象用/运算符连接层级跨平台无缝工作。它还提供.name文件名、.suffix扩展名、.resolve()绝对路径等常用属性方法。说实话用熟了以后你会觉得以前用字符串拼路径的方式是在自己给自己挖坑。4. 异常处理别再让程序“裸奔”4.1 异常是什么捕获它的正确姿势程序运行过程中出现错误时Python会抛出异常。如果不处理程序就像一个人走着走着突然心脏骤停——直接崩掉。异常处理就是你给程序装的“安全气囊”和“急救包”。基础写法是try/excepttry: num int(input(请输入数字: )) result 100 / num print(f结果是: {result}) except ValueError: print(输入的不是有效数字) except ZeroDivisionError: print(不能除以0)这里有个很重要的原则except要精确捕获你能预料的异常类型而不是写一个什么都能接住的裸except:。因为裸except:不仅会掩盖程序bug还会把KeyboardInterrupt用户按CtrlC这类本不该捕获的异常也吞掉导致程序没法正常退出。我见过不少同学为了省事写except Exception as e: print(e)一包到底这种写法用于调试可以上生产就要谨慎。4.2 自定义异常和异常传播一个健壮的程序通常会定义自己的异常类型。比如你写一个成绩统计系统分数不能超过100那就可以定义一个InvalidScoreErrorclass InvalidScoreError(Exception): 分数超出合法范围的异常 def check_score(score): if score 0 or score 100: raise InvalidScoreError(f分数不合法: {score}) return score自己抛异常时用raise关键字。这样调用方就能try/except你的自定义异常进行针对性处理。合理使用自定义异常能把复杂项目的错误结构变得清晰。也要理解异常的传播机制如果你在某个函数里没有处理异常它会逐层往上抛直到有try/except接住为止。所以异常处理可以放在最外层统一做内层函数只管正常流程和抛出异常即可。但你要有个判断哪些异常应该在当前层就处理掉哪些应该继续抛给上层决定。我通常把握一个原则能恢复、有明确处理方式就在当前层处理否则就抛出去。4.3 常用异常排查技巧处理异常最实用的技巧之一是用traceback模块查看完整调用栈。在except块里import traceback try: risky_operation() except Exception: traceback.print_exc() # 打印完整堆栈信息traceback.print_exc()会打印从异常源头到捕获点的完整调用链定位问题比只看一行错误信息快得多。另一个技巧是用logging.exception记录异常日志这在写脚本工具时特别有用——你可以把出错信息和完整堆栈一并写入日志文件方便事后复盘。5. 面向对象入门从“按步骤办事”到“仿真建模”5.1 理解类和对象别再死记定义很多Python教材把类和对象讲得非常抽象新手听完一脸懵。我的理解方式一直是用生活场景类比类是模子对象是用模子印出来的具体产品。类定义了属性有什么特征和方法能干什么对象则是一个具体的实例。比如你要做一个图书管理系统你可以定义一个Book类class Book: def __init__(self, title, author, price): self.title title self.author author self.price price def desc(self): return f《{self.title}》 作者{self.author} 价格{self.price}元__init__是构造方法创建对象时自动调用。注意第一个参数永远是self指的是对象自身。创建对象就一行book1 Book(三体, 刘慈欣, 99)然后print(book1.desc())。不要小看这个封装的意义。如果没有类你要同时管理十本书的信息可能得建十个字典每个字段名重复十次。用类之后每本书就是一个对象属性和行为都收拢在一起代码整个就顺了。5.2 三个核心特征封装、继承、多态面向对象的三个核心特征我是用实际项目经验来理解的。封装就是把数据和操作数据的方法捆绑在一起外部不需要知道内部实现细节。调用book.desc()时你不需要关心它内部是怎么拼接字符串的。对外暴露一个方法内部实现随意改这就是封装最实用的价值。继承就是“子类自动拥有父类的属性和方法”。比如图书系统除了Book还有EBook电子书和PaperBook纸质书它们都是书有很多共性就可以继承Book只写自己独特的差异部分class EBook(Book): def __init__(self, title, author, price, formatPDF): super().__init__(title, author, price) self.format format def desc(self): base super().desc() return f{base} 格式{self.format}super()是用来调用父类方法的关键字。这里覆盖了desc()方法实现了不同子类的不同行为——这就是多态的体现同一个方法名在不同对象上有不同的表现。多态让代码写起来更灵活你可以统一调用.desc()不用管它具体是什么类型的书每个对象会自动执行自己的版本。5.3 类和对象的实战写法参考面向对象不是学完理论就完事关键是写的时候能自然用起来。我给你一个综合场景批量管理一批学生成绩并生成统计报告。这个项目里可以设计两个类一个是学生一个是成绩处理器class Student: def __init__(self, name, scores): self.name name self.scores scores def average(self): return sum(self.scores) / len(self.scores) class GradeManager: def __init__(self): self.students [] def add_student(self, student): self.students.append(student) def top_student(self): return max(self.students, keylambda s: s.average()).name def class_average(self): if not self.students: return 0.0 total sum(s.average() for s in self.students) return round(total / len(self.students), 1)这个设计很简单但它体现了面向对象的组织思路数据学生姓名、成绩和方法求平均分放在一起一个完整的业务概念就是一个类。以后系统扩展添加“不及格统计”“分数段分布”等功能都改在对应的类里加方法就行不会把代码逻辑搅成一锅粥。6. 实操完成一个成绩统计小工具6.1 需求梳理和方案设计理论讲再多不如自己动手写一个。我们拿一个非常常见的场景练手从CSV文件读取学生成绩做统计把结果写入一个新的CSV文件。这个需求本身不复杂但完整覆盖了文件读写、函数封装、异常处理、类和对象几个知识点。假设待处理的input.csv结构是这样姓名,平时分,期末分 小明,85,90 小红,78,92 小刚,66,74需求很简单计算每个学生的综合成绩平时分占40%期末分占60%统计班级平均分按综合成绩排序输出到result.csv。整个方案我分成三块负责读取的模块、负责计算的模块、负责写入的模块。这样清晰分工也方便以后单独替换某一层。考虑到这个工具要给非技术同学用如果文件名输错了、文件内容格式不对都应该给出友好的中文提示而不是扔一堆堆栈信息。6.2 完整代码实现过程先看入口部分主流程里把读取、计算、写入串起来并处理异常import csv from pathlib import Path class ScoreTool: def __init__(self, input_file, output_file): self.input_file Path(input_file) self.output_file Path(output_file) def read_scores(self): if not self.input_file.exists(): raise FileNotFoundError(f找不到文件: {self.input_file}) records [] with open(self.input_file, r, encodingutf-8-sig, newline) as f: reader csv.DictReader(f) for row in reader: records.append(row) return records def compute(self, records): results [] for row in records: name row[姓名] daily float(row[平时分]) final float(row[期末分]) total round(daily * 0.4 final * 0.6, 1) results.append({姓名: name, 综合分: total}) results.sort(keylambda x: x[综合分], reverseTrue) avg sum(item[综合分] for item in results) / len(results) return results, round(avg, 1) def write_result(self, results, avg): with open(self.output_file, w, encodingutf-8-sig, newline) as f: fieldnames [姓名, 综合分] writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() for row in results: writer.writerow(row) writer.writerow({姓名: 班级平均分, 综合分: avg}) def run(self): records self.read_scores() results, avg self.compute(records) self.write_result(results, avg) print(f处理完成共 {len(results)} 名学生班级平均分 {avg})然后在入口处做异常处理if __name__ __main__: tool ScoreTool(input.csv, result.csv) try: tool.run() except FileNotFoundError as e: print(f错误{e}) except ValueError: print(错误成绩文件内容格式不正确请检查是否有非数字内容) except Exception as e: print(f未知错误{e})关于csv.DictReader它自动读取首行作为字段名后续每一行以字典形式返回字段名直接对应表头代码可读性很好。如果CSV文件不大用这个方式最爽。文件头读取时的编码用了utf-8-sig就是为了兼容带BOM的文件避免首列名出现\ufeff前缀的坑。6.3 代码运行结果和扩展思路直接运行这段代码result.csv生成的内容如下姓名,综合分 小红,86.4 小明,85.0 小刚,70.8 班级平均分,80.7程序同时会在控制台输出一行摘要。这样一套下来即使完全不懂技术的人双击运行脚本就能拿到统计结果体验上更像一个“小工具”而不是“代码片段”。这套结构也很容易扩展。你想加“最高分”“最低分”“合格率”这些指标只要在compute方法里加几行统计在write_result里加几个字段你想支持录入新增的“加分项”就在读取时多读一列。因为核心逻辑都按类和函数拆开了加功能就像给模块插上新接口不会牵连别的部分。7. 常见问题速查第三阶段避坑清单7.1 函数和文件操作高频报错我在带人练这个阶段时收集了几个出现频率很高的报错。这里做成速查表遇到直接对照看报错信息原因解决UnicodeDecodeError打开文件时编码不对open()里显式指定encodingutf-8FileNotFoundError路径写错了或文件不存在使用Path.exists()预先判断检查相对路径的起点UnboundLocalError函数内使用了外部变量且同名赋值发生冲突改成参数传递或明确用global不推荐ValueError: I/O operation on closed file在with块外访问文件对象确保文件读写都放在with块内PermissionError文件被其他程序占用或没有写权限关闭占用程序或换个输出目录7.2 面向对象初学者的高频疑惑面向对象容易遇到“能看懂但不会设计”的问题。我给个实用的起步建议先别追求复杂的设计模式从“一个业务概念对应一个类”开始。例如一个系统里有“用户”和“订单”两个概念就建两个类属性和相关操作都放进去。至于继承先用“把重复代码抽到父类”的思路什么时候发现多个类大量重复了再考虑抽一个父类出来这种自然驱动出来的继承设计比一开始就画一大张类图靠谱得多。另一个常见疑问是函数和类到底怎么选我的判断标准很简单如果你处理的是“一组操作流程”函数就够了如果你需要保存状态、反复调用且状态在调用之间要保持或者一个对象有固定属性和行为就选类。纯函数式写法追求“输入输出干净”这对小程序完全够用程序规模大了以后类是更好的组织工具。7.3 调试技巧和习惯建议写第三阶段程序最大的敌人不是语法错而是逻辑错。语法错了IDE会直接标红逻辑错了程序能运行但结果不对。我习惯用“三步定位法”先打印关键变量的值确认数据长什么样再逐步推进看数据在哪一步开始偏离预期最后修正逻辑后故意引入一个错误数据测试程序的表现。我在这个阶段特别推荐养成一个习惯每个脚本开头手动指定版本兼容参数重要代码旁写一行注释说明为何这么写。比如编码为什么用utf-8-sig分隔符为什么这样指定。这些注释不是废话是给三天后的自己看的。你很快就会发现理解自己以前写过的代码往往是最难的。慢慢做到这些等你开始尝试写爬虫处理HTML网页、用脚本批量重命名文件、做数据清洗时你会发现基础3阶段这些东西都在背后默默撑着你。每个函数、每次异常捕获、每个类封装都是在为更复杂的问题打地基。地基稳了上面盖什么楼都不慌。

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询