今天你 AI 赋能了吗?打开浏览器,在地址栏输入 xuefulu.ai ,按下 Enter 即可访问学府路1号!
2026年10月14日 星期三 10:40:26
常用网址: | | 学府路1号 https://xuefulu.ai | 下载 Python https://xuefulu.ai/Python.html | 下载 TRAE https://xuefulu.ai/Trae.html

一、什么是异常

异常(Exception)就是程序运行的时候撞上的错误。比如:

Python
# 下面这行会直接让程序崩溃
print(10 / 0)

不处理的话,屏幕会刷出一大段红色报错,程序当场停止,后面该做的事全都没做。

异常处理的本意很简单:出了问题别让整个程序死掉,兜住它、告诉用户原因、还能接着往下走。

没处理异常处理了异常
程序崩溃,后面的代码都不跑了只跳过出错的这一段,其余照常运行
用户看到一堆看不懂的英文报错用户看到一句人话提示,体验更好
小错拖垮整个程序程序更稳,更适合交给别人用
记住一句话:异常不是你的错,是程序在说「这里我搞不定了」。Python 用「抛出异常」的方式把问题抛出来,我们要做的就是用 try...except 把它接住。

二、先看看不处理会怎样(5 个真实报错)

先看 5 个最常见、不处理就会崩的场面。下面每一段都是真跑出来的报错原文(只看最后一行那句英文就知道错在哪):

① 除以零

Python
print(10 / 0)
报错
ZeroDivisionError: division by zero

② 把字母当数字

Python
int('abc')
报错
ValueError: invalid literal for int() with base 10: 'abc'

③ 打开不存在的文件

Python
open('不存在的文件.txt')
报错
FileNotFoundError: [Errno 2] No such file or directory: '不存在的文件.txt'

④ 字符串和数字相加

Python
print('hello' + 5)
报错
TypeError: can only concatenate str (not "int") to str

⑤ 调不存在的方法

Python
print('abc'.append('x'))
报错
AttributeError: 'str' object has no attribute 'append'
这些报错都有一个共同结构:异常类型 + 冒号 + 一句英文说明。比如 ValueError 表示「值的类型对、但内容不合适」,FileNotFoundError 表示「文件没找到」。学会看这一行,你就已经能看懂大半报错。

课程思政|工匠精神与严谨求实 · 报错原文里就有答案

不处理异常时,屏幕会甩出一大段红色文字,但它其实很有礼貌:ValueError、FileNotFoundError 这些类型名加一句说明,已经告诉你错在哪。养成习惯,先自己把最后一行读完,再去问人。能读懂报错的人,解决问题的速度快十倍——这也是遇事先查证、不臆断的严谨做派。

三、最简单的 try...except

try...except 是最核心的写法。把「可能出错的代码」放进 try,把「出错后咋办」放进 except:

Python
try:
    print(10 / 0)
except ZeroDivisionError:
    print('错误:不能除以零!')

运行结果:

输出
错误:不能除以零!

注意看发生了什么:10 / 0 本来会崩,但被 except ZeroDivisionError: 接住了,于是只打印一句人话提示,程序继续往下走。

写成含义
try:后面缩进的代码,「我担心这里会出错」
except ZeroDivisionError:如果 try 里抛出的是「除以零」这种错,就执行我下面缩进的代码
print('...')出错后做的事,必须缩进

一步一步照着写

  1. 写 try:(末尾冒号不能少)。
  2. 下一行缩进 4 格,写可能出错的代码。
  3. 另起一行写 except 异常类型:,下面缩进写「兜底」代码。
只抓「具体那种错」最安全。上面只接 ZeroDivisionError,如果是别的错(比如把字母当数字),它管不了,程序还是会崩——这是好事,能让你看清到底是哪种错。

四、捕获多种异常(多个 except、从具体到通用的顺序)

一段代码可能同时犯好几种错。比如让用户输入两个数相除,用户可能:输字母(ValueError)、输 0(ZeroDivisionError)。用多个 except 分别接住:

Python
try:
    a = int(input('请输入被除数 a: '))
    b = int(input('请输入除数 b: '))
    print('结果:', a / b)
except ValueError:
    print('错误:请输入整数!')
except ZeroDivisionError:
    print('错误:除数不能为零!')

运行效果(输入 6 和 0):

输出
请输入被除数 a: 请输入除数 b: 错误:除数不能为零!

顺序很重要:具体的在前,通用的在后

Python 会从上往下找第一个能对得上的 except。所以要把具体的异常写在前面,把「兜底的大网」写在最后:

Python
try:
    x = float(input('请输入数字: '))
    y = float(input('请输入数字: '))
    print(x / y)
except ValueError:
    print('输入的不是数字')
except ZeroDivisionError:
    print('除数不能为零')
except Exception as e:
    print('其他错误:', e)
千万别把 except Exception 写在最前面!它什么错都接,后面的具体 except 就永远轮不到执行,等于白写。顺序错了,提示也会变得很笼统。
口诀:先抓小错(具体类型),再抓大错(通用类型)。就像先找具体的人,找不到再找「其他人」。

五、except Exception as e(拿到错误信息)

有时你想「不管什么错,都接住,并把错误信息打印出来」——用 except Exception as e。as e 把异常对象存进变量 e,方便你看详情:

Python
try:
    f = open('test.txt', 'r')
except Exception as e:
    print('发生了一个错误:', e)
    print('错误类型:', type(e).__name__)

运行结果(文件不存在时):

Python
发生了一个错误: [Errno 2] No such file or directory: 'test.txt'
错误类型: FileNotFoundError

两个常用小技巧

写法作用
str(e)异常的说明文字,比如「division by zero」
type(e).__name__异常的名字,比如「ZeroDivisionError」

试一下:

Python
try:
    result = 1 / 0
except Exception as e:
    print('异常名字:', type(e).__name__)
    print('异常说明:', str(e))

输出:

输出
异常名字: ZeroDivisionError
异常说明: division by zero
别滥用!except Exception 会把所有错都吞掉,连你代码里的拼写错误也一起藏起来,调试时会非常痛苦。平时写练习可以图省事,正式项目里要尽量写清楚具体是哪种异常。

AI 助手小贴士|「什么都捕获」是异常处理第一反模式,让 AI 摆翻车现场。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:为什么不建议直接写 except Exception?给两个「真错误被吞掉」的翻车例子,再讲怎么改成精确捕获

课程思政|诚信与职业操守 · 别把错误藏进地毯下

except Exception 什么错都接,写起来省事,代价却是连自己拼错的变量名也一并被吞掉,调试时找不到北。把错误悄悄咽下去,问题不会消失,只会攒成大坑。工作中也一样:数据对不上、进度有偏差,第一时间说出来,比事后解释来得体面——坦诚是职业操守的底线。

六、else 与 finally(执行顺序对照表)

try 还能配两个搭档:else 和 finally。

子句什么时候执行典型用途
try总先执行,放可能出错的代码——
excepttry 里出错时才执行报告错误、补救
elsetry 里没出错才执行放「正常结果」的处理
finally不管出没出错都执行关文件、清理资源

看一遍完整结构

Python
try:
    num = int(input('请输入一个数字: '))
    result = 10 / num
    print('结果是:', result)
except ValueError:
    print('输入的不是有效数字!')
except ZeroDivisionError:
    print('不能除以零!')
else:
    print('没有异常时执行(else)')
finally:
    print('不管怎样,我都会执行')

输入 2 时:

输出
请输入一个数字: 结果是: 5.0
没有异常时执行(else)
不管怎样,我都会执行

输入 abc 时:

输出
请输入一个数字: 输入的不是有效数字!
不管怎样,我都会执行
else 比把代码硬塞进 try 更干净:只有「确实成功了」才做后续处理,不容易误伤。而 finally 像「善后小弟」——无论成败都来收尾,最常用于关掉打开的文件。

七、主动抛异常 raise

有些错不是 Python 发现的,是你自己想主动拦下来的。比如年龄不能是负数——用 raise 自己制造一个异常:

Python
def set_age(age):
    if age < 0:
        raise ValueError('年龄不能为负数!')
    print('年龄设置为:', age)

try:
    set_age(-5)
except ValueError as e:
    print('捕获到错误:', e)

运行结果:

输出
捕获到错误: 年龄不能为负数!

raise 的意思是「这里我认定是错的,直接报错」。常用在函数入口做检查:进来的值不对,立刻拦下,别让错误数据跑进后面的逻辑。

场景怎么用 raise
参数不合法raise ValueError('说明')
类型不对raise TypeError('说明')
自定义的业务错误raise 自己的异常类('说明')
把 raise 想成「拉响警报」:你发现情况不对,主动按铃,让调用你的人用 except 来接。这样错误原因写在你自己手里,特别清楚。

课程思政|数据安全与法治素养 · 脏数据,别放它进门

年龄不能是负数,就在函数入口用 raise ValueError 把它拦下来,不让错误数据流进后面的计算。这道关卡看着麻烦,却是数据质量的第一道防线。入口把好关,后面才省心——将来接入别人的数据源时,先校验、再使用,既不替错误数据背书,也是对自己统计结论负责。

八、自定义异常

当 Python 自带的异常名字不够贴切时,你可以造一个自己的。方法很简单:新建一个类,继承 Exception,里面写 pass 就行:

Python
class PasswordTooShortError(Exception):
    pass

def check_password(password):
    if len(password) < 6:
        raise PasswordTooShortError('密码长度至少6位!')
    return '密码合格'

try:
    check_password('123')
except PasswordTooShortError as e:
    print('密码错误:', e)

运行结果:

输出
密码错误: 密码长度至少6位!

自定义异常让报错一眼就知道是哪一类问题。比如收到 PasswordTooShortError,你立刻知道是密码太短,而不是一个笼统的 ValueError。

别忘了 class PasswordTooShortError(Exception): 里的 pass 和冒号。空类里至少要有一句 pass,否则会报 IndentationError。
命名习惯:异常类名末尾加 Error(如 NegativeNumberError、ConfigLoadError),一看就是异常。

九、常见内置异常速查表

下表是新手最常遇到的几种异常。看名字基本就能猜到错在哪:

异常类型什么时候出现生活化理解
ValueErrorint('abc')、值不合适类型对,但内容不对
TypeError'a' + 5、类型不对把两种不搭的东西硬凑一起
ZeroDivisionError10 / 0除数写了 0
IndexErrorlst[99] 越界下标超出了列表长度
KeyErrord['不存在']字典里没有这个键
FileNotFoundError打开不存在的文件文件找不着
AttributeError'abc'.append('x')这个对象没有这个方法

用法小结

Python
# 抓具体错,对症下药
try:
    num = int(user_input)
except ValueError:
    print('请输入数字')
写程序时可以先想「这一步最可能出哪种错」,就写对应的 except。不确定的时候,先用 except Exception as e: 把错误名字打出来(type(e).__name__),就知道该抓哪一种了。

十、十个基础案例(对应 10 道习题)

下面 10 个小案例都来自课堂习题,建议每个都亲手敲一遍。每个案例都给了「示例运行结果」,对照着看。

案例 1:分账时的除零错误

Python
try:
    total = int(input('请输入奶茶总价: '))
    people = int(input('请输入人数: '))
    result = total / people
    print('每人分摊:', result)
except ZeroDivisionError:
    print('人数不能为 0!')

输入 60 和 0 时输出:

输出
请输入奶茶总价: 请输入人数: 人数不能为 0!

案例 2:处理无效输入(ValueError)

Python
try:
    cups = float(input('请点奶茶杯数: '))
    print('你点了', cups, '杯')
except ValueError:
    print('请输入数字!')

输入 3.5 时输出:

输出
请点奶茶杯数: 你点了 3.5 杯

案例 3:同时处理多种异常

Python
try:
    total = int(input('请输入总价: '))
    cups = int(input('请输入杯数: '))
    print('每杯:', total / cups)
except ValueError:
    print('错误:请输入整数!')
except ZeroDivisionError:
    print('错误:杯数不能为 0!')

输入 6 和 0 时输出:

输出
请输入总价: 请输入杯数: 错误:杯数不能为 0!

案例 4:通用异常与 finally 块

Python
try:
    with open('今日订单.txt', 'r') as f:
        print(f.read())
except FileNotFoundError:
    print('文件未找到!')
finally:
    print('操作完成')

文件不存在时输出:

输出
文件未找到!
操作完成

案例 5:else 子句的使用

Python
try:
    total = int(input('请输入总价: '))
    cups = int(input('请输入杯数: '))
    print('每杯:', total / cups)
except ValueError:
    print('错误:请输入整数!')
except ZeroDivisionError:
    print('错误:杯数不能为 0!')
else:
    print('输入有效')
finally:
    print('结束')

输入 10 和 2 时输出:

输出
请输入总价: 请输入杯数: 每杯: 5.0
输入有效
结束

案例 6:主动抛出异常(raise)

Python
def check_cups(cups):
    if cups <= 0 or cups > 100:
        raise ValueError('杯数不合理')
    print('杯数合法')

try:
    check_cups(-5)
except ValueError as e:
    print('异常信息:', e)

输出:

输出
异常信息: 杯数不合理

案例 7:自定义异常类

Python
class NotEnoughStockError(Exception):
    pass

def order_safe(cups):
    if cups > 10:
        raise NotEnoughStockError('库存不足,最多 10 杯')
    return '下单成功'

try:
    result = order_safe(20)
except NotEnoughStockError as e:
    print('自定义异常:', e)

输出:

输出
自定义异常: 库存不足,最多 10 杯

案例 8:捕获所有异常(谨慎使用)

Python
try:
    result = 1 / 0
except Exception as e:
    print('发生了异常')
    print('异常类型:', type(e).__name__)
    print('异常信息:', str(e))

输出:

输出
发生了异常
异常类型: ZeroDivisionError
异常信息: division by zero

案例 9:异常链(raise ... from)

Python
class RecipeLoadError(Exception):
    pass

try:
    with open('配方.txt', 'r') as f:
        data = f.read()
except FileNotFoundError as e:
    raise RecipeLoadError('配方文件加载失败') from e

运行结果(最后一行是真正的异常类型):

报错
RecipeLoadError: 配方文件加载失败

真跑时,完整报错里在这行上面还有一段 FileNotFoundError 和一行 The above exception was the direct cause of the following exception——这正是 from e 干的事:把「最初为什么错」钉在新异常下面。平时读报错,仍然从最后一行看起。

案例 10:综合应用——安全分账函数

Python
def split_bill(total, people):
    if not isinstance(total, (int, float)) or not isinstance(people, (int, float)):
        raise TypeError('参数必须是数字')
    if people == 0:
        raise ZeroDivisionError('人数不能为零')
    return total / people

try:
    result = split_bill('60', 2)   # 故意传字符串测试
    print('结果:', result)
except TypeError as e:
    print('类型错误:', e)
except ZeroDivisionError as e:
    print('除零错误:', e)

输出:

输出
类型错误: 参数必须是数字

AI 助手小贴士|会语法不等于会决策:什么该捕、用什么捕,练判断。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 3 道「该不该捕获、用什么捕获」的决策题:文件打不开、用户输入非数字、除以零,让我先说方案你再审

十一、常见坑

坑 1:空的 except 会藏 bug

Python
try:
    num = int(input('请输入数字: '))
except:
    pass    # 什么都不做,错误被悄悄吞掉

except:(不带任何类型)会接住所有异常,连你拼写变量名写错都一并掩盖,调试时根本找不到问题。

尽量写清楚要抓哪种异常,比如 except ValueError:。非要兜底,也至少写成 except Exception as e: 把错误打出来。

坑 2:except 的顺序写反了

把通用异常写在前面,后面的具体异常就成了摆设:

Python
try:
    x = int('abc')
except Exception:
    print('出错了')
except ValueError:
    print('这不是数字')    # 永远执行不到

坑 3:忘记写冒号和缩进

Python
try
    print(1)
except ValueError
    print('错')

两处都会报错:try 和 except 末尾都要冒号,下面的代码都要缩进 4 格。

坑 4:在 except 里又抛出新错

Python
try:
    print(10 / 0)
except ZeroDivisionError:
    print('出错啦' + 1)    # 这里又踩了 TypeError

处理异常的代码本身也可能出错。遇到嵌套问题,可以再套一层 try,或者先用 print 把变量看清楚。

好习惯:先把可能出错的代码缩小到最小,只把真正「外界不可控」的部分(用户输入、文件、网络)放进 try,其余逻辑留在外面,异常才好定位。

坑 5:用异常代替正常的 if 判断

Python
# 不推荐:为了判断键存不存在而故意报错
try:
    value = d['name']
except KeyError:
    value = '默认值'

字典里有没有某个键,直接用 if 'name' in d: 判断更高效、更清楚。异常是用来应对「意外」的,不是用来走正常流程的。

十二、报错手册

下面这些报错都是真跑出来的原文,照抄自本机解释器。哪天屏幕弹出来眼熟的,直接对照处理。

① 除零

Python
print(10 / 0)
报错
ZeroDivisionError: division by zero

原因:除数写成了 0。检查公式或先判断 b != 0 再除。

② 字符串转数字失败

Python
int('abc')
报错
ValueError: invalid literal for int() with base 10: 'abc'

原因:int() 只认数字字符串。用户输入时套一层 try...except ValueError。

③ 文件找不到

Python
open('不存在的文件.txt')
报错
FileNotFoundError: [Errno 2] No such file or directory: '不存在的文件.txt'

原因:路径或文件名写错,文件还没创建。确认文件确实存在,或用 w 模式让程序自己建。

④ 字符串加数字

Python
print('hello' + 5)
报错
TypeError: can only concatenate str (not "int") to str

原因:字符串和数字不能直接相加。要么 str(5),要么用 f-string:print(f'hello{5}')。

⑤ 调了不存在的方法

Python
print('abc'.append('x'))
报错
AttributeError: 'str' object has no attribute 'append'

原因:字符串没有 append 方法(那是列表的)。看清对象类型再调方法。

⑥ 列表下标越界

Python
lst = [1, 2, 3]
print(lst[99])
报错
IndexError: list index out of range

原因:下标超过列表长度。列表只有 3 个元素,下标最大是 2。

⑦ 字典里没有这个键

Python
d = {'name': '小明'}
print(d['age'])
报错
KeyError: 'age'

原因:键 'age' 不存在。用 d.get('age', '默认值') 或先 if 'age' in d: 判断。

⑧ 解包个数对不上

Python
a, b = [1, 2, 3]
报错
ValueError: too many values to unpack (expected 2)

原因:等号左边 2 个变量,右边有 3 个值。变量个数必须和序列长度一致。

⑨ 类型不对

Python
print('年龄:' + 18)
报错
TypeError: can only concatenate str (not "int") to str

原因:和 ④ 一样是「字符串 + 数字」。18 要写成 str(18) 或用 f-string。

⑩ 主动 raise 的自定义错误

Python
raise ValueError('年龄不能为负数!')
报错
ValueError: 年龄不能为负数!

原因:这是你自己在代码里 raise 出来的,不是 Python 的 bug,按提示检查传入的值即可。

AI 助手小贴士|traceback「从下往上看」是规矩,但初学时让 AI 逐行讲更快——讲多了,你自己就会读了。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:这是我遇到的完整 traceback。请从最后一行往上逐行解释含义,指出最根源的错误在哪一行、怎么修,最后教我怎么自己读 traceback:<粘贴完整报错>

十三、随堂自测(10 题)

点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。

已答 0 / 10 · 正确 0 —

1下面哪种写法能防止 10 / 0 让程序崩溃?

  • A. 用 if 判断一下
  • B. 用 try...except 接住异常
  • C. 用 print() 打印
  • D. 用 # 注释掉
答案:B。try...except 专门用来接住运行时的异常;if 只能预判已知情况,print 和注释都拦不住崩溃。

2int('abc') 会抛出哪种异常?

  • A. IndexError
  • B. KeyError
  • C. ValueError
  • D. TypeError
答案:C。int() 只能转换像 '123' 这样的数字字符串,'abc' 内容不合适,报 ValueError(值的类型对、但内容不对)。

3有多个 except 时,更具体的异常(如 ValueError)应该放在哪里?

  • A. 放在最后
  • B. 放在最前面
  • C. 放在中间随便
  • D. 放哪都一样
答案:B。Python 从上往下找第一个对得上的 except。具体异常放前面先接住,通用异常(如 Exception)放最后兜底,顺序反了就白写。

4except Exception as e: 中,type(e).__name__ 返回什么?

  • A. 异常的类名(字符串,如 'ZeroDivisionError')
  • B. 出错的行号
  • C. 整个报错堆栈
  • D. None
答案:A。type(e).__name__ 拿到异常类型的名字(字符串),str(e) 才是那句说明文字。两者配合能看清错在哪。

5else 子句在 try 结构里什么时候执行?

  • A. 只要发生异常就执行
  • B. 程序刚启动时执行
  • C. try 里没出错时才执行
  • D. 循环结束时执行
答案:C。else 只在 try 整段都顺利、一个异常都没抛时才执行,常用来放「成功后的正常处理」。

6finally 子句什么时候会执行?

  • A. 只有没异常时才执行
  • B. 不管有没有异常都会执行
  • C. 只有出错时才执行
  • D. 程序最末尾才执行
答案:B。finally 是「善后小弟」,无论成败都执行,最适合用来关文件、释放资源这类清理工作。

7raise 关键字的作用是?

  • A. 捕获异常
  • B. 忽略异常
  • C. 主动抛出异常
  • D. 定义一个函数
答案:C。raise 让你在发现「值不合法」时主动拉响警报,比如 raise ValueError('年龄不能为负数'),把错误原因攥在自己手里。

8自定义一个异常类,应该继承谁?

  • A. Exception
  • B. object
  • C. BaseException 随便选
  • D. 不需要继承任何类
答案:A。自定义异常统一继承 Exception(如 class MyError(Exception): pass),这样它才能被普通 except 正常接住。

9打开一个不存在的文件,会报什么错?

  • A. ValueError
  • B. KeyError
  • C. TypeError
  • D. FileNotFoundError
答案:D。路径或文件名写错、文件还没创建时,都会报 FileNotFoundError。确认文件存在,或用 w 模式让它自动创建。

10print('hello' + 5) 运行时会怎样?

  • A. 正常打印 hello5
  • B. IndexError
  • C. KeyError
  • D. TypeError
答案:D。字符串和数字不能直接相加,报 TypeError。要拼接得用 str(5) 或 f-string:print(f'hello{5}')。