讲解
程序运行中出错时,Python 会「抛出异常」:int('abc') 抛 ValueError、打开不存在的文件抛 FileNotFoundError、除以零抛 ZeroDivisionError、访问不存在的键抛 KeyError。没人处理的异常会一直向上传播,最终让程序崩溃并打印 traceback(错误堆栈)。traceback 从下往上读:最后一行是异常类型和信息,往上是调用链,学会读它是调试的基本功。
try/except 是处理异常的结构:把可能出错的代码放进 try,出错后跳转匹配的 except 分支执行,程序不再崩溃。except 后面要写具体的异常类型——except ValueError: 只抓值错误;裸 except: 或 except Exception: 会把所有错误(包括你没想到的 bug)都吞掉,是明确反对的写法。完整形态还有 else(try 没出错时执行)和 finally(出不出错都执行,做清理):try 里只放真正可能抛错的最小代码块,块越大越容易误抓。
主动抛出异常用 raise:raise ValueError('年龄不能为负')。标准库定义了丰富的异常类型,自定义异常写一个继承 Exception 的空类即可:class ConfigError(Exception): pass。Python 的错误处理哲学是 EAFP——「先做再说,出事道歉」:与其 if 一串检查再动手(LBYL),不如直接做、用 try 兜住异常,这在文件、网络等外部资源场景尤其合理,因为检查和执行之间状态可能已经变了。
示例
# 基本形态:try / except / else / finally
def parse_age(text):
try:
age = int(text)
except ValueError:
print(f'{text!r} 不是有效数字')
return None
else:
print(f'解析成功:{age}')
return age
finally:
print('(finally:无论成败都会执行)')
parse_age('18')
print('---')
parse_age('abc')
# 捕获多种异常 + 拿到异常对象
def read_number(path):
try:
with open(path, 'r', encoding='utf-8') as f:
return int(f.read().strip())
except FileNotFoundError:
print(f'文件 {path} 不存在')
except ValueError as exc:
print(f'文件内容不是数字:{exc}')
return None
print('读不存在的文件:', read_number('不存在的文件.txt'))
# 主动抛出异常
def set_age(age):
if age < 0:
raise ValueError(f'年龄不能为负:{age}')
return age
try:
set_age(-3)
except ValueError as exc:
print('抓到主动抛出的异常:', exc)
# 自定义异常
class ConfigError(Exception):
"""配置相关错误。"""
def load_config(data):
if 'host' not in data:
raise ConfigError('缺少必需的 host 字段')
return data
try:
load_config({'port': 8080})
except ConfigError as exc:
print('配置错误:', exc)
# EAFP 风格:直接做,出事再兜
d = {'a': 1}
try:
print('EAFP 取值:', d['b'])
except KeyError:
print('键不存在,走兜底逻辑')
常见坑
- 裸 except 吞掉一切:except: 连 KeyboardInterrupt 和真正的 bug 一起吞,问题被掩盖到无法排查。永远写具体类型。
- try 块太大:把十行代码全塞进 try,抛错时根本不知道是哪一行出的问题。try 里只放可能抛错的那一两行。
- except 里只写 pass:「出错了就当无事发生」比崩溃更可怕——数据悄悄错了你还不知道。至少要打印日志。
- 用异常控制正常流程:拿 KeyError 当 if 用偶尔是 EAFP 的合理写法,但循环里依赖 ZeroDivisionError 之类做分支就是设计失误了。
小结
try/except/else/finally 抓具体异常,raise 主动抛,自定义异常继承 Exception,哲学是 EAFP。下一节进入面向对象:类与对象。