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 - 常用内置模块(待更新)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1146336.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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

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

Multisim14使用教程:负反馈电路构建指南

Multisim14实战指南&#xff1a;手把手教你搭建负反馈放大电路你有没有过这样的经历&#xff1f;想做一个音频前置放大器&#xff0c;查了一堆资料&#xff0c;画好了原理图&#xff0c;买齐了元件&#xff0c;结果一通电——输出全是噪声&#xff0c;或者干脆饱和成一条直线。…

系统学习vh6501测试busoff的电气参数设置

如何用 VH6501 精准测试 ECU 的 Bus-Off 行为&#xff1f;从电气参数到实战配置全解析在汽车电子开发中&#xff0c;你有没有遇到过这样的场景&#xff1a;某个 ECU 突然“失联”&#xff0c;总线通信中断&#xff0c;但硬件没断电、电源正常——这很可能是它进入了Bus-Off状态…

高速PCB设计中的EMI抑制措施详解

高速PCB设计中的EMI抑制&#xff1a;从根源到实战的系统性思考在今天这个“速度即正义”的电子时代&#xff0c;一块PCB板子上跑着GHz级别的信号早已不是稀罕事。FPGA、高速ADC/DAC、DDR4/5内存、千兆以太网、LVDS摄像头……这些模块齐聚一堂&#xff0c;带来了前所未有的性能提…

Etalon(标准具)在光谱仪中的应用

Etalon&#xff08;标准具&#xff09;是一种基于多光束干涉原理的高精度光学元件。它通常由两块高度平行、部分反射的平板组成&#xff0c;中间是固定距离的空气或固体介质。它在光谱仪中扮演着核心滤波和选频的角色&#xff0c;是现代高分辨率光谱技术的基石之一。一. 核心原…

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

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

电感选型实战案例:电源电路中的应用

电感选型实战&#xff1a;从理论到落地&#xff0c;搞定电源中的“能量搬运工”你有没有遇到过这样的情况&#xff1f;一个看似设计完整的DC-DC电源电路&#xff0c;样机一上电&#xff0c;输出电压不稳、纹波超标&#xff0c;带载后电感发热严重&#xff0c;甚至芯片反复触发过…

nrf52832的mdk下载程序失败原因全面讲解

nrf52832的MDK下载失败&#xff1f;别慌&#xff0c;一文搞懂所有坑点与解法你是不是也遇到过这种情况&#xff1a;Keil MDK 点了“Download”按钮&#xff0c;结果弹出一个冷冰冰的提示——No Cortex-M device found或Flash Download Failed&#xff1f;对着 nrf52832 开发板反…

pjsip项目起步:跨平台构建方法详解

pjsip 构建实战&#xff1a;从零开始掌握跨平台编译的“正确姿势”你有没有过这样的经历&#xff1f;满怀信心地克隆下pjproject源码&#xff0c;运行./configure&#xff0c;结果终端里跳出一连串红色错误&#xff1a;ALSA headers not foundundefined reference to pthread_c…

Jenkins监听Git仓库的底层原理

想象一下这样的工作场景: 你是团队里的“打包小能手”,整天盯着 Git 仓库: develop 分支有新提交?记一下,要打个测试包 release 分支打了 tag?嗯,这要打一版预发布 某人合并了一个大功能?最好自动跑一遍测试 如果全靠你“人盯人”刷新 Git 网页,迟早疯。 于是大家会…

Altium Designer在温度控制系统中的项目应用

从原理到量产&#xff1a;用 Altium Designer 打造高精度温度控制系统 在工业自动化、医疗设备和精密仪器领域&#xff0c;一个稳定可靠的温度控制系统&#xff0c;往往决定了整台设备的性能上限。无论是恒温培养箱、半导体工艺加热平台&#xff0c;还是高端家电中的智能温控模…

NAS设备搭载USB3.2速度极限测试报告

NAS外接USB3.2速度为何跑不满&#xff1f;一次说清全链路性能真相你有没有遇到过这种情况&#xff1a;花大价钱买了支持“USB3.2 Gen 2x2”的NAS&#xff0c;又配了顶级NVMe SSD移动硬盘&#xff0c;结果拷贝4K视频时速度只有三四百MB/s&#xff0c;甚至还不如几年前的老设备&a…

Unity自动化构建:CI/CD解放打包人

文章摘要 本文介绍如何通过CI/CD工具实现Unity项目的自动化构建流程,解放人工打包工作。通过这套自动化方案,开发者只需提交代码,后续构建分发流程将由CI/CD系统自动完成,显著提升开发效率。 先把画面想象出来: 你是 Unity 项目里的“那位可怜的打包担当”。 每次提测:…

OpenBMC与ASPEED HWMON驱动集成方法论讲解

OpenBMC中ASPEED HWMON驱动集成实战指南&#xff1a;从设备树到sysfs的全链路解析 你有没有遇到过这样的场景&#xff1f;BMC系统已经跑起来了&#xff0c;IPMI也能连上&#xff0c;但风扇转速读出来一直是0 RPM——明明硬件接好了&#xff0c;信号也测过是正常的。或者更糟&am…

Jenkins 或其它 CI 服务器上,一个“自动打 Android 测试包”的按钮背后的脚本。

文章摘要 这篇文章详细解释了Jenkins上自动构建Android测试包的脚本实现。主要内容包括: 脚本首先通过git命令拉取最新代码,确保构建基于最新代码 使用Unity命令行工具进行无界面批量构建,指定项目路径和构建方法 将生成的APK文件复制到统一下载目录 脚本采用bash编写,设…

ES6模块化实战:结合Babel实现兼容性解决方案

用现代语法&#xff0c;跑在老浏览器上&#xff1a;ES6模块化 Babel 的实战落地之道你有没有遇到过这样的场景&#xff1f;刚写完一段优雅的import { useStore } from ./store&#xff0c;信心满满地打开 IE11 测试——结果控制台直接报错&#xff1a;“SyntaxError: ‘import…

操作指南:如何读懂继电器模块电路图中的控制路径

如何真正看懂继电器模块电路图&#xff1a;从信号到动作的完整控制链你有没有过这样的经历&#xff1f;手握一块继电器模块&#xff0c;接到单片机上&#xff0c;代码写好了&#xff0c;通电后却“啪”一声响——继电器不吸合、MCU重启&#xff0c;甚至烧了IO口。打开电路图一看…

低功耗RS232硬件电路设计从零实现

低功耗RS232电路设计&#xff1a;如何让“老古董”接口跑进物联网时代&#xff1f; 你有没有遇到过这样的尴尬&#xff1f; 在开发一款电池供电的工业传感器时&#xff0c;客户坚持要用RS232通信——理由是“我们的上位机系统用了20年&#xff0c;不能换”。你心里一沉&#x…

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

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

USB转485驱动硬件架构深度剖析:电平转换核心原理

USB转485驱动硬件架构深度剖析&#xff1a;电平转换核心原理在工业自动化、智能楼宇与电力监控系统中&#xff0c;尽管以太网和无线通信日益普及&#xff0c;RS-485依然稳坐“工业现场总线老兵”的宝座。它抗干扰强、传输距离远&#xff08;可达1200米&#xff09;、支持多点通…