news 2026/5/1 6:51:14

Day 17:【99天精通Python】异常处理 - 让程序稳如泰山

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 17:【99天精通Python】异常处理 - 让程序稳如泰山

Day 17:【99天精通Python】异常处理 - 让程序稳如泰山

前言

欢迎来到第17天!

在编程的世界里,不出 bug 是不可能的。即便是最顶尖的程序员,也无法保证代码永远不出错。用户可能会输入非法数据,文件可能突然被删除,网络可能会断开。

如果程序一遇到错误就直接崩溃(闪退),用户体验会非常差。异常处理 (Exception Handling)就是给程序穿上一层"防弹衣",当意外发生时,我们能优雅地捕获错误,并给出友好的提示,而不是让程序直接挂掉。

本节内容:

  • 什么是异常?
  • try...except基本结构
  • 捕获多种异常
  • elsefinally的作用
  • 主动抛出异常raise
  • 自定义异常类
  • 实战练习

一、什么是异常 (Exception)?

异常是指程序在运行过程中发生的错误,它会打断指令的正常执行。

常见的异常类型

  • SyntaxError: 语法错误(通常在运行前就会报错)。
  • NameError: 尝试访问一个未定义的变量。
  • TypeError: 类型不匹配(例如数字 + 字符串)。
  • ZeroDivisionError: 除数为 0。
  • IndexError: 列表索引越界。
  • KeyError: 字典中不存在该键。
  • FileNotFoundError: 打开不存在的文件。

二、基本语法:try…except

我们使用try块包含可能出错的代码,用except块来处理错误。

2.1 基本结构

try:# 可能会报错的代码num=int(input("请输入一个数字: "))result=10/numprint(f"计算结果:{result}")exceptZeroDivisionError:# 当发生除以0错误时执行print("错误:除数不能为0!")exceptValueError:# 当输入的不是数字时执行print("错误:请输入有效的整数!")

2.2 获取异常信息

有时我们需要知道具体的报错信息,可以使用as关键字。

try:print(1/0)exceptZeroDivisionErrorase:print(f"捕获到错误:{e}")# 捕获到错误: division by zero

2.3 捕获所有异常 (慎用)

可以使用Exception捕获所有类型的运行时错误。但通常不推荐这样做,因为这会掩盖一些意想不到的 bug(比如变量名写错)。

try:# 复杂逻辑passexceptExceptionase:print(f"发生未知错误:{e}")

三、完善结构:else 与 finally

异常处理还有两个可选的子句:elsefinally

  • else: 当try没有发生异常时执行。
  • finally:无论是否发生异常,最终都会执行(常用于清理资源,如关闭文件、断开数据库)。
defdivision_test(x,y):try:result=x/yexceptZeroDivisionError:print("Error: 除数不能为0")else:print(f"Success: 结果是{result}")finally:print("--- 执行完毕 ---")print("第一次测试:")division_test(10,2)print("\n第二次测试:")division_test(10,0)

运行结果

第一次测试: Success: 结果是 5.0 --- 执行完毕 --- 第二次测试: Error: 除数不能为0 --- 执行完毕 ---

四、主动抛出异常:raise

有时候,并不是代码出错了,而是业务逻辑不满足,我们需要主动报错,通知调用者。这时使用raise关键字。

defset_age(age):ifage<0orage>150:# 主动抛出一个值错误raiseValueError("年龄必须在 0 到 150 之间")print(f"年龄设置为:{age}")try:set_age(200)exceptValueErrorase:print(f"设置失败:{e}")

五、自定义异常

虽然 Python 内置了很多异常,但在大型项目中,为了更清晰地表达业务错误,我们通常会自定义异常类。只需要继承Exception类即可。

# 定义一个"余额不足"异常classInsufficientFundsError(Exception):passclassBankAccount:def__init__(self,balance):self.balance=balancedefwithdraw(self,amount):ifamount>self.balance:# 抛出自定义异常raiseInsufficientFundsError(f"余额不足!当前:{self.balance}, 需要:{amount}")self.balance-=amountprint(f"取款成功,剩余:{self.balance}")account=BankAccount(100)try:account.withdraw(200)exceptInsufficientFundsErrorase:print(f"交易拒绝:{e}")

六、实战练习

练习1:健壮的整数输入器

编写一个函数get_integer(prompt),循环提示用户输入内容,直到用户输入一个合法的整数为止。

defget_integer(prompt):whileTrue:user_input=input(prompt)try:value=int(user_input)returnvalue# 成功转换,返回结果并退出循环exceptValueError:print(f"输入无效:'{user_input}' 不是一个整数,请重试。")# 测试age=get_integer("请输入你的年龄: ")print(f"你的年龄是:{age}")

练习2:安全的文件读取

编写一个程序读取文件,如果文件不存在,提示用户;如果文件编码错误,也提示用户;无论如何最后都要打印"操作结束"。

defsafe_read(filename):try:withopen(filename,"r",encoding="utf-8")asf:print(f.read())exceptFileNotFoundError:print(f"错误: 文件 '{filename}' 未找到。")exceptUnicodeDecodeError:print(f"错误: 文件 '{filename}' 编码格式不对,无法读取。")exceptExceptionase:print(f"发生未知错误:{e}")finally:print("--- 读取操作结束 ---")# 测试safe_read("not_exist.txt")

七、常见问题

Q1:try块里应该放多少代码?

越少越好。只把可能报错的那几行关键代码放进去。如果在try里放了几百行代码,一旦报错,你很难定位到底是哪一行出了问题。

Q2:except可以不写类型吗?

可以写except:,但这等同于捕获所有异常(包括Ctrl+C中断)。这是一种极坏的编程习惯,强烈建议至少写except Exception:

Q3:returnfinally谁先执行?

如果try块中有returnfinally会在return之前执行。千万不要在finally里面写return,否则会覆盖掉原本的返回值。


八、小结

无错误

发生异常

异常处理流程

try: 执行代码

执行 else (可选)

匹配 except 类型?

执行 except 块 (处理错误)

向上抛出异常 (程序崩溃)

执行 finally (无论如何都执行)

其他核心

raise: 主动报错

自定义异常: 继承 Exception

关键要点

  1. 预判错误:用try...except包裹可能出错的代码。
  2. 精准捕获:尽量指定具体的异常类型(如ValueError),避免使用笼统的Exception
  3. 善后处理:资源释放(如关闭文件)一定要放在finally中。
  4. 业务逻辑:不满足条件时,可以用raise主动中断程序。

九、课后作业

  1. 简易除法器:编写程序接收两个用户输入的数字进行除法运算。需要处理:输入非数字、除数为0、以及未知错误。
  2. 列表越界保护:编写一个函数safe_get(lst, index),接收列表和索引。如果索引有效,返回对应的元素;如果索引越界,捕获IndexError并返回None(而不是报错)。
  3. 用户注册系统:定义一个register(username, password)函数。
    • 如果用户名长度小于3,抛出自定义异常UsernameTooShortError
    • 如果密码长度小于6,抛出自定义异常PasswordTooShortError
    • 编写调用代码,捕获并打印这些错误。

下节预告

Day 18:常用内置模块 (JSON, Datetime, Random)- 之前简单提过标准库,明天我们将深入讲解开发中最离不开的几个模块,学会处理时间、数据交换和随机事件。


系列导航

  • 上一篇:Day 16 - 面向对象编程(下)
  • 下一篇:Day 18 - 常用内置模块(待更新)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 4:07:10

基于SiFinite的RISC-V调试模块指令支持全面讲解

深入SiFinite RISC-V调试模块&#xff1a;如何实现精准到指令的非侵入式调试&#xff1f;你有没有遇到过这样的场景&#xff1f;系统偶尔死机&#xff0c;日志没留下任何线索&#xff1b;或者在RTOS中任务莫名其妙卡住&#xff0c;加打印反而让问题消失——典型的“观察者效应”…

作者头像 李华
网站建设 2026/4/18 8:45:05

USB接口双设备切换电路:多路复用设计方案

一“键”切换双主机&#xff1a;如何用多路复用器实现稳定USB设备共享&#xff1f;你有没有过这样的经历&#xff1f;办公桌上两台电脑来回切换&#xff0c;每次都要拔插U盘、键盘、鼠标&#xff1b;嵌入式开发时&#xff0c;调试器在Windows和Linux主机间反复插拔&#xff1b;…

作者头像 李华
网站建设 2026/4/25 1:42:09

嵌入式设备中动态screen切换逻辑设计

嵌入式UI进阶&#xff1a;如何打造流畅的动态Screen切换系统&#xff1f;你有没有遇到过这样的场景&#xff1f;在一款工业HMI设备上点击“设置”按钮&#xff0c;界面卡顿半秒才跳转&#xff1b;或者医疗设备从主界面进入数据图表页时&#xff0c;画面撕裂、文字闪烁。这些看似…

作者头像 李华
网站建设 2026/4/18 10:58:36

Day 15:【99天精通Python】面向对象编程(OOP)中篇 - 封装、继承与多态

Day 15&#xff1a;【99天精通Python】面向对象编程(OOP)中篇 - 封装、继承与多态 前言 欢迎来到第15天&#xff01; 在昨天的课程中&#xff0c;我们学会了如何定义类和创建对象。但这只是 OOP 的冰山一角。面向对象编程之所以强大&#xff0c;归功于它的三大核心特性&#xf…

作者头像 李华
网站建设 2026/4/18 9:44:56

逻辑门组合电路设计:超详细版基础入门指南

从零开始学数字电路&#xff1a;用逻辑门搭建你的第一个组合电路你有没有想过&#xff0c;手机里每秒执行数十亿条指令的处理器&#xff0c;其实是由最简单的“开关”一步步搭起来的&#xff1f;这些“开关”不是物理按钮&#xff0c;而是我们今天要讲的主角——逻辑门。在嵌入…

作者头像 李华
网站建设 2026/4/28 5:03:48

电商市场的用户反馈分析与应用

电商市场的用户反馈分析与应用 关键词:电商市场、用户反馈分析、文本挖掘、情感分析、数据应用 摘要:本文聚焦于电商市场的用户反馈分析与应用。在电商行业竞争日益激烈的当下,用户反馈蕴含着巨大的价值。通过对用户反馈的深入分析,电商企业能够了解用户需求、改进产品与服…

作者头像 李华