先看一个"事故现场"。运行下面这段代码,用户输入 abc:
num = int(input("请输入一个数字:")) # 用户输入了"abc"
print(100 / num)
输出(用户输入 abc 时):
请输入一个数字: abc
Traceback (most recent call last):
File "demo.py", line 1, in <module>
num = int(input("请输入一个数字:"))
ValueError: invalid literal for int() with base 10: 'abc'
最后一行 ValueError: ... 就是异常的名字和说明:值不对,"abc" 转不成整数。程序到这里就停了,后面所有代码都不执行——这就是"崩溃"。
你的程序就像走钢丝的人,随时可能遇到意外:用户该输数字却输了文字、文件明明不存在却去读、算除法却除以 0。异常处理就是给程序配一根安全绳:try 是"我去尝试走",except 是"摔下来我接住"。用大白话说,就是提前告诉程序:这里可能会出事,出事了你别崩,按我说的处理。学了它,你的程序就从"一碰就碎"变成"摔倒了能自己爬起来",还会友好地告诉用户"你输错了,请重来"。
今天最后还会学一个超实用的组合拳:while True + try/except,做成"输入错了就重新输入"的校验器——明天做学生信息管理系统全靠它保护。
什么是异常:程序"摔倒"的那一下
程序运行中出现意外,Python 会"抛出一个错误",这个错误就叫异常(Exception)。不处理,程序当场崩溃,打印一大段红色报错然后停止。
每个异常都有名字(类型),报错信息的最后一行就是它的名字,比如 ValueError、ZeroDivisionError、FileNotFoundError。名字告诉你出的是哪种事。其实你之前见过的所有报错都是异常:NameError(名字没定义)、TypeError(类型不对)、IndexError(下标越界)……它们都是异常这个大家族的一员。
关于异常,先记住三点:
- 异常发生在运行期,不是写代码时:语法错误是写的时候就报,异常是程序跑起来才可能遇到,所以必须靠"运行时保护"。
- 看报错先看最后一行:红色报错一大堆,最关键的是最后一行
异常名: 说明,它告诉你出了什么事。 - 异常会让程序直接停:一旦抛出且没被接住,后面的代码全部不执行,所以"可能出错"的地方要提前兜住。
try / except:把可能出错的地方"兜住"
结构就三行起底:
try:
可能出错的代码
except 错误类型:
出错后执行的代码
try 里面的代码正常执行;一旦抛出了 except 后面写的那个类型的错误,程序不崩溃,直接跳到 except 块里执行里面的代码,然后继续往下走。try 里没出错时,except 块不会执行。
把刚才那个"事故现场"救活:
try:
num = int(input("请输入一个数字:"))
print(100 / num)
except ValueError:
print("你输入的不是数字!")
print("程序没有崩溃,继续运行")
用户输入 abc 时输出:
请输入一个数字: abc
你输入的不是数字!
程序没有崩溃,继续运行
用户输入 10 时输出:
请输入一个数字: 10
10.0
程序没有崩溃,继续运行
注意上面的 except ValueError 只接得住"输入不是数字"这一种情况:如果用户输入 0,100 / 0 抛的是 ZeroDivisionError,它没被接住,程序照样崩。所以一个 try 可以配多个 except,这就是下一节的内容。
try/except 有三个细节:
except后面要写错误类型:except ValueError:后面有冒号,块内要缩进。- 类型写错就接不住:用户输
abc抛的是ValueError,你写except TypeError:就接不住,照样崩。不确定时用后面的Exception兜底。 try里出错位置之后的代码不执行:第一行就出错,后面几行不会跑,直接跳except。
多个 except + else + finally:完整版四件套
完整的异常处理结构有四个部分,各管一件事:
- 多个
except:不同类型错误不同处理方案。从上往下匹配,命中一个就不再往后找。 else:try里没有出错才执行(相当于"没出事时顺带干点活")。finally:不管有没有错都会执行(收尾专用:关文件、打印提示等)。
执行顺序可以这么记:先试 try;出错了进对应的 except,没出错进 else;最后不管怎样都进 finally。
try:
num = int(input("请输入数字:"))
result = 100 / num
except ValueError:
print("你输入的不是数字!")
except ZeroDivisionError:
print("不能除以 0!")
else:
print("计算完成,结果是", result) # 只有没出错才执行
finally:
print("这句话不管有没有错都会执行")
用户输入 abc:
请输入数字: abc
你输入的不是数字!
这句话不管有没有错都会执行
用户输入 0:
请输入数字: 0
不能除以 0!
这句话不管有没有错都会执行
用户输入 10:
请输入数字: 10
计算完成,结果是 10.0
这句话不管有没有错都会执行
三个容易记混的点:
- 顺序不能乱:
try→except(可多个)→else→finally,else必须跟在所有except后面。 else和except互斥:出错走except,不出错走else,同一轮只会走一个。finally一定会执行:哪怕except里写了break/return,finally也会先跑完再走。新手阶段先把finally当"收尾区"理解就行。
except Exception as e:保命兜底
不确定会出什么错时,可以用 except Exception as e: 接住所有异常:Exception 是大部分异常的老祖宗(所有异常都算它的后代),as e 把异常对象存进变量 e,print(e) 就能看到出错原因。这是"兜底"写法:先保证程序不崩,再根据打印的原因决定要不要细化。
try:
num = int(input("请输入数字:"))
result = 100 / num
print(result)
except Exception as e:
print("出错了:", e) # e 里装着出错原因
用户输入 abc:
请输入数字: abc
出错了: invalid literal for int() with base 10: 'abc'
用户输入 0:
请输入数字: 0
出错了: division by zero
先把 e 打印出来,你就知道具体是哪种错误,再决定要不要拆成多个 except 分别处理。
有个反例要特别提醒:别写光秃秃的 except:(不写类型也不写 as e)。这样出错了你完全不知道原因,等于把错误信息吞了,调试起来非常痛苦。至少写 except Exception as e:。
三个细节:
Exception大写 E:写exception报NameError(内置类名首字母大写)。- 别用空
except::错误原因被吞,出了 bug 无从查起。 - 兜底不是终点:
Exception适合"先保证不崩",正式产品里最好还是细化成具体异常类型,各管各的。
组合拳:while True + try/except 做输入校验
有个需求特别常见:让用户输入一个 1~100 的数字,输错了(输文字、输超范围)就提示重来,直到输对为止。实现靠组合拳:
while True:死循环,一直问。try/except:输入非法(抛ValueError)就continue重新问。break:输入合法就退出循环。
这就像一台"验钞机":不验合格不放过。明天做学生管理系统、第 6 周项目里所有"输入成绩、输入选项"的地方都靠它。
while True:
try:
num = int(input("请输入一个 1~100 的数字:"))
if num < 1 or num > 100:
print("超出范围,请重新输入")
continue # 重新进入循环再问一次
break # 合法,跳出循环
except ValueError:
print("输入的不是数字,请重新输入")
print("输入合法,你输入的是", num)
跑一遍完整的对话:
请输入一个 1~100 的数字: abc
输入的不是数字,请重新输入
请输入一个 1~100 的数字: 150
超出范围,请重新输入
请输入一个 1~100 的数字: 50
输入合法,你输入的是 50
这套组合拳有三个细节:
continue和break别搞混:continue是"再来一轮"(重问),break是"结束循环"(通过)。范围不对用continue,合法才break。num在循环外使用:能保证循环结束时num一定被赋值过(只有break能出来,所以安全),但注意别在except分支里用num——它没被赋值。- 先转类型再查范围:
int(input(...))写在try里,输文字抛ValueError被接住;范围检查放在try内、break前。
随堂练习
-
练习一(掷骰子加保险,必做):把周二的"掷骰子"升级:用户输入要掷几次,如果输入的不是数字(比如字母),提示"请输入数字"而不是崩溃。 提示:
int(input(...))放进try,except ValueError打印提示;输入合法后再for循环掷。 -
练习二(输入校验 1~100):写一个循环,反复让用户输入一个 1~100 的数字,输入非法(非数字或超范围)就提示重新输,直到正确为止。 提示:
while True+try/except ValueError+continue/break;照上面组合拳的模板改一改就行。 -
练习三(读文件兜底):尝试读一个不存在的文件(比如
not_exist.txt),用try/except捕获,提示"文件不存在",程序不崩溃。 提示:open放在try里,except FileNotFoundError:打印友好提示;文件存在时正常读出内容。 -
★ 进阶题(计算器防呆版):给之前写过的计算器(加、减、乘、除)加异常处理:用户输的不是数字 → 提示重输;除数为 0 → 提示"不能除以 0";用
while True让用户选"继续 or 退出"。 提示:把"输入两个数字"封装成带校验的代码块;除法函数里if b == 0直接提示,或用try/except ZeroDivisionError。
拿不准异常类型?问 AI
场景:不知道 except 后面该写什么
提示词:
我有一段代码,运行时报错:ValueError: invalid literal for
int() with base 10。我不确定该用哪种异常。请解释这个报错的
意思,并告诉我常见异常类型(ValueError、ZeroDivisionError、
FileNotFoundError 等)分别什么时候出现。我是初学者,用大白话。
场景:写输入校验
提示词:
帮我写一段 Python 代码:让用户输入一个 1~100 的数字,
输错了(不是数字或超范围)就提示重新输入,直到正确为止。
要求用 while True + try/except 的写法,加中文注释,
代码尽量简单。我是初学者。
场景:解释 try/except/else/finally
提示词:
用大白话给我讲清楚 try、except、else、finally 四个部分的
执行顺序:什么情况下执行 else?finally 是干什么用的?
给我一个简单例子说明。我是零基础,请讲得越直白越好。
常见报错速查
| 报错信息 | 什么意思 | 怎么办 |
|---|---|---|
ValueError: invalid literal for int() with base 10: 'abc' |
把不能转数字的文字转 int | 用 try/except ValueError 接住,提示用户重新输入 |
ZeroDivisionError: division by zero |
除以 0 | 除之前先判断,或用 try/except ZeroDivisionError |
FileNotFoundError: [Errno 2] |
文件不存在 | 确认文件已生成;读文件用 try/except FileNotFoundError 兜底 |
except: 空捕获(不报错但很坑) |
把所有错误吞了,看不到原因 | 改成 except Exception as e: print(e) 至少能打印原因 |
NameError: name 'exception' is not defined |
把 Exception 拼成小写 |
内置异常类名首字母大写:Exception |
收个尾,明天见
今天的主线就一条:程序会摔倒,但可以教会它爬起来。四件套 try 试 → except 接住 → else 没出错才执行 → finally 不管怎样都执行;不确定错误类型就用 except Exception as e: print(e) 兜底,千万别写空空的 except:;输入校验用 while True + try/except + break。
明天是本周最后一天:学 JSON 数据格式,然后把这周的函数、文件、异常、JSON 全部串起来,用 AI 协作完成"学生信息管理系统"。异常处理就是明天项目里的"安全护栏"——你现在可以猜猜,明天那个系统里,哪些输入最容易让程序"摔倒"?