上一章我们已经认识了异常,知道了程序为什么会在运行过程中突然报错、突然中断。现在问题来了。
如果程序一出错就直接停掉,那很多场景其实是很难用的。
比如用户输错了一个年龄,程序就整个结束。 比如文件一时没找到,后面的流程全都停下。 比如数据里混进了一条坏记录,整个批处理任务全部报废。
这时候,就需要 try...except 出场了。
它的作用,不是让错误凭空消失,而是让程序在遇到异常时,不要那么狼狈地崩掉,而是按你设计好的方式去处理问题。
说得直白一点,try...except 就是在告诉程序:
我知道这里有可能出错。 如果真的出错了,不要立刻翻车。 先按我的规则处理一下。
这就是所谓的优雅报错。
一、为什么一定要学 try...except
很多新手刚接触异常处理时,会有一种想法:
既然代码会报错,那我小心一点写不就行了,为什么还要专门学异常处理。
这个想法只对了一半。
小心写代码当然很重要,但现实世界不会永远配合你。 用户可能输错内容。 文件可能临时缺失。 网络可能突然断开。 某条数据可能格式不对。
你不能要求所有输入永远正确,也不能保证所有环境永远稳定。 所以真正成熟的程序,不只是能在理想情况下跑通,还要能在出问题时不至于完全失控。
try...except 的价值,就在这里。
它不是帮你逃避错误,而是帮你接住错误。
二、先看最基本的写法
最简单的结构如下:
try: 可能出错的代码except: 出错后执行的代码
先不要急着背,先把它翻译成人话。
try 的意思是:先试试看。except 的意思是:如果试的时候出错了,就执行这里的处理逻辑。
看一个最简单的例子:
try: print(10 / 0)except: print('程序出错了')
运行后,程序不会像以前那样直接抛出一大段报错然后停掉,而是会输出:
程序出错了
这就说明,异常被接住了。
前面那个除以 0 的错误,确实还是发生了。 只不过这次程序没有把错误直接甩到你脸上,而是按照 except 里的逻辑做了处理。
三、你要真正理解的,不是语法,而是控制流程
很多人看到 try...except,只记住了写法,却没理解它到底改变了什么。
它改变的,是程序出错时的走向。
没有 try...except 的时候,程序一旦遇到异常,通常就直接停掉。 有了 try...except 以后,程序遇到异常时,可以先跳到 except 里执行处理逻辑。
也就是说,它给了程序一个备用方案。
正常情况下,程序按主流程走。 出错情况下,程序切到异常处理流程。
你可以把它想成开车时的应急方案。 平时路况正常,就按原路线开。 一旦前方封路,就走备用路线。try...except 干的,就是这件事。
四、try 里到底该放什么
这点非常重要。
try 里放的,应该是你判断为有可能出错的代码。 不是整份程序一股脑全塞进去,也不是为了图省事把几十行都包起来。
比如:
age_text = input('请输入年龄:')try: age = int(age_text) print(age + 1)except: print('你输入的年龄格式不对')
这里最可能出问题的点,就是 int(age_text)。 因为如果用户输入的是数字,没事。 如果输入的是别的内容,比如中文或者字母,就可能报错。
所以把这一段放进 try,就很自然。
异常处理的第一原则,其实就是:
只包住你真正担心会出错的那部分。
这样代码会更清楚,后面定位问题也更方便。
五、一个特别常见的实际场景:处理用户输入
下面这个例子,几乎每个初学者都应该亲手写一遍。
age_text = input('请输入你的年龄:')try: age = int(age_text) print('明年你的年龄是:', age + 1)except: print('输入有误,请输入数字')
如果用户输入:
18
程序会输出:
明年你的年龄是: 19
如果用户输入:
十八
程序则不会崩掉,而是输出:
输入有误,请输入数字
这就是 try...except 最直观的价值。
它不是让错误变没,而是让程序在错误出现时,还能给出更友好的反馈。
这件事对用户体验非常重要。 因为用户不关心你内部报了什么 ValueError,他只想知道自己下一步该怎么做。
六、except 为什么不能只理解成报个提示
很多人刚学到这里,会觉得 except 不就是打印一句提示吗。其实远不止。
except 里可以做很多事,比如:
提示用户重新输入 记录日志 跳过这次错误继续处理下一条数据 给出默认值 结束当前操作但不中断整个程序
也就是说,except 不是一个单纯的报错显示区,而是一个真正的异常处理区。
看一个稍微实用一点的例子:
scores = ['90', '85', 'abc', '88']for item in scores:try: score = int(item) print('成绩是:', score)except: print('发现一条无效成绩,已跳过:', item)
输出结果会是:
成绩是: 90成绩是: 85发现一条无效成绩,已跳过: abc成绩是: 88
如果没有 try...except,程序处理到 abc 这里就会直接停。 但现在程序能跳过这条坏数据,继续处理后面的内容。
这已经非常接近真实开发场景了。
七、异常处理不是掩盖问题,而是有意识地处理问题
这里一定要避免一个误区。
有些人学了 try...except 以后,看到哪都想套一个,仿佛这样代码就安全了。其实不是。
如果你只是机械地把错误包住,却不关心为什么错、错了以后怎么处理,那就不叫异常处理,只能算把问题藏起来。
比如这段代码:
try: result = 10 / 0except:pass
程序确实不会报错了。 但这段代码的问题在于,错误发生了,你却什么都没做。 程序表面安静,内部其实已经出了问题。
所以要记住:
异常处理的重点不是别报错,而是报错以后怎么办。
如果你接住了异常,却没有合理处理,那很多时候只是让问题更隐蔽。
八、为什么正式代码不推荐直接写裸 except
刚才为了帮助你入门,我们用了最简单的写法:
try: ...except: ...
这种写法能用,但不是最推荐的方式。 因为它会把所有异常一股脑接住。
更常见、更规范的写法,是指定异常类型。
比如:
try: age = int(input('请输入年龄:')) print(age + 1)except ValueError: print('输入格式不正确,请输入数字')
这里的意思是:
如果这里发生的是 ValueError,就执行下面的处理逻辑。
为什么这样更好?
因为不同异常,代表的问题不同。 你只处理你真正预期到的那种异常,代码会更准确,也更安全。
比如用户输入年龄时,最常见的问题就是数字转换失败。 这时捕获 ValueError 就很合理。
这就像医生看病一样。 你不能不管什么情况都开同一种药。 得先知道是哪类问题,再做对应处理。
九、最常见的几个异常类型,要开始慢慢认识了
在 try...except 里,你以后会很常见到这些类型:
ValueError值的形式不对。比如把一段不是数字的文本转成整数。
TypeError类型不支持当前操作。比如字符串和整数直接相加。
IndexError列表下标越界。
KeyError字典里没有对应的键。
FileNotFoundError文件不存在。
你现在还是不用死背,但从这一章开始,要慢慢形成一个意识:
异常不仅有报错这个动作,还有具体类型。
而异常类型,往往决定你该怎么处理它。
十、文件处理里 try...except 特别常用
前面我们学文件时,已经见过一种高频风险:文件不存在。
比如:
with open('data.txt', 'r', encoding='utf-8') as f: content = f.read() print(content)
如果 data.txt 不存在,程序就会直接报错。
现在我们可以给它加上异常处理:
try:with open('data.txt', 'r', encoding='utf-8') as f: content = f.read() print(content)except FileNotFoundError: print('文件不存在,请检查文件名或路径')
这样一来,程序就不会因为文件没找到而直接崩掉,而是会给出更清晰的提示。
这个例子特别有代表性。 因为它说明,异常处理不只是给用户输入兜底,也是在给外部环境兜底。
十一、try 成功时,except 会不会执行
不会。
这一点你必须非常清楚。
try 里的代码如果顺利执行,没有发生异常,那么 except 就会被跳过。
比如:
try: print(10 / 2)except: print('出错了')
输出结果是:
5.0
这里不会打印出错了。 因为根本没出错。
所以 try...except 并不是二选一硬执行,而是根据运行情况动态分流:
没出错,走 try 的正常流程。 出错了,跳去 except 的异常流程。
这就是它的本质。
十二、try 里一旦某行出错,后面会怎么样
这个问题特别关键。
看下面这段代码:
try: print('开始执行') num = int('abc') print('这行还能执行吗')except ValueError: print('发生了值错误')
输出会是:
开始执行发生了值错误
中间那句:
print('这行还能执行吗')
不会执行。
原因很简单。try 代码块一旦某一行触发异常,当前这个 try 里后面的代码通常就不继续走了,而是直接跳到匹配的 except。
这点你一定要想明白。 因为它会影响你如何组织代码结构。
也就是说,try 不是说里面每一行都一定跑完。 它是试着往下执行,一旦撞上异常,就立刻切去异常处理分支。
十三、一个很典型的小案例:让用户反复输入直到正确
虽然这一章重点是 try...except 基础,但我们可以先看一个很实用的简单组合。
whileTrue: text = input('请输入一个整数:')try: num = int(text) print('你输入的整数是:', num)breakexcept ValueError: print('输入无效,请重新输入')
这段代码的意思是:
用户输入内容 程序尝试转成整数 如果成功,就打印并结束循环 如果失败,就提示重新输入
这就是异常处理和循环配合的经典用法。
现实里很多输入场景,都会这么写。 因为你无法保证用户第一次就输入正确,但你可以让程序有能力温和地引导他继续输入。
这比程序一报错就退出,要强太多了。
十四、别把所有逻辑都塞进 try
这是一个特别需要提前提醒的习惯问题。
有些初学者喜欢这样写:
try: text = input('请输入数字:') num = int(text) result = 100 / num print(result)except: print('出错了')
这段代码不是不能跑,但问题在于,它把多个可能出错的点全混在一起了。
到底是输入转换失败了。 还是除数变成了 0。 还是别的问题。
你用一句笼统的出错了,很难判断具体原因。
更清晰的思路是:
把可能出错的点拆开想。 预期什么错误,就尽量明确处理什么错误。
也就是说,异常处理写得好不好,不只是技术问题,也是代码表达能力的问题。
十五、try...except 的第一层价值,是让程序更友好
这一章你先不要把它想得太复杂。
在入门阶段,try...except 最直接的价值,就是让程序在出错时别那么生硬。
以前程序一错就是一大段报错。 现在你可以把它变成一句人能看懂的话。
比如:
请输入正确的数字 文件没有找到,请检查路径 这条数据格式不对,已跳过
这种变化看起来简单,但本质上已经是在从写给自己看的代码,往写给别人用的程序迈进了。
真正的程序,不只是自己能跑,更要让别人用起来不难受。
十六、第二层价值,是让程序更有韧性
除了更友好,try...except 还有一个非常重要的价值,就是让程序更能扛事。
比如批量处理 1000 条数据。 如果第 237 条脏了,难道前面 236 条和后面几百条都不要了?
显然不合理。
所以很多时候,我们会希望:
这一条错了,记下来,跳过。 后面的继续处理。
这就是异常处理让程序拥有的韧性。 它不再是一碰就碎,而是出现局部问题时还能维持整体运行。
虽然你现在还在学基础,但这个思维从现在开始建立,非常重要。
十七、一个文件读取的小练习案例
下面这段代码,建议你自己动手跑一下。
filename = input('请输入要读取的文件名:')try:with open(filename, 'r', encoding='utf-8') as f: content = f.read() print('文件内容如下:') print(content)except FileNotFoundError: print('文件不存在,请重新检查文件名')
这个例子很适合入门,因为它同时练到了三件事:
用户输入 文件读取 异常处理
如果文件存在,就正常读取。 如果文件不存在,就给出提示。
这已经很像一个真正能和用户交互的小工具了。
十八、本章你最该建立的意识
到这里,你一定要真正记住下面这句话:
异常处理不是为了假装程序没错,而是为了让程序在出错时仍然有控制感。
控制感这个词很重要。
没有 try...except 时,程序一旦报错,控制权就丢了。 有了它以后,程序至少还能按你的规则来响应问题。
你决定提示什么。 你决定是否跳过。 你决定是否继续。 你决定是否结束。
这就是程序从脆弱走向可靠的第一步。
十九、本章小练习
你可以立刻做两个练习。
练习 1 让用户输入一个数字,并输出 100 除以这个数字的结果。 如果用户输入的不是数字,就提示输入错误。
练习 2 让用户输入一个文件名,并尝试读取。 如果文件不存在,就提示文件没找到。
参考代码如下:
text = input('请输入一个数字:')try: num = int(text) print(100 / num)except ValueError: print('输入的不是有效整数')except ZeroDivisionError: print('不能输入 0')
再看文件版:
filename = input('请输入文件名:')try:with open(filename, 'r', encoding='utf-8') as f: print(f.read())except FileNotFoundError: print('没有找到这个文件')
这两个练习非常经典。 你把它们真正敲一遍,try...except 的基本感觉就建立起来了。
二十、本章总结
这一章,我们正式进入了异常处理的核心入口。
try 表示先尝试执行一段可能出错的代码。except 表示一旦发生异常,就执行对应的处理逻辑。 它不会让错误消失,但能让程序避免直接崩掉。 异常处理不是掩盖问题,而是更有意识地处理问题。 实际开发中,更推荐捕获明确的异常类型,而不是一股脑使用裸 except。try...except 最重要的价值,一是让程序对用户更友好,二是让程序在出错时更有韧性。
下一章我们继续往前走,把异常处理结构补完整:else 和 finally:异常处理的完整结构。