Python错误与异常
大约 4 分钟
错误与异常
语法错误
语法错误(SyntaxError)是最基本的错误类型,Python 执行代码前会先解析语法,语法有问题时直接报错,程序不会运行。
# 缺少冒号
if True print("hello")
# SyntaxError: expected ':'
异常
语法正确,但执行阶段发生错误,称为异常(Exception)。异常不一定会导致程序崩溃,可以在代码中捕获并处理。
# 常见的内置异常
10 * (1 / 0) # ZeroDivisionError: division by zero
4 + spam * 3 # NameError: name 'spam' is not defined
"2" + 2 # TypeError: can only concatenate str (not "int") to str
arr = [1, 2]
arr[5] # IndexError: list index out of range
int("abc") # ValueError: invalid literal for int() with base 10: 'abc'
异常处理
1. try/except
# 捕获指定类型的异常
try:
print(10 / 0)
except ZeroDivisionError:
print("除数不能为0") # 除数不能为0
2. 捕获异常对象
在异常名称后添加 as 关键字,可以拿到异常实例,输出详细的错误信息。
try:
print(10 / 0)
except ZeroDivisionError as err:
print("捕获到异常:", err) # 捕获到异常: division by zero
3. 捕获多个异常
# 方式1:多个except分支,分别处理
try:
int("abc")
except ZeroDivisionError:
print("除数不能为0")
except ValueError:
print("值转换失败") # 走这个分支
# 方式2:元组形式,多个异常统一处理
try:
print(10 / 0)
except (ZeroDivisionError, ValueError) as err:
print("统一处理:", err) # 统一处理: division by zero
4. else 子句
else 子句在 try 块没有抛出任何异常时执行,适合放置「依赖前面代码成功执行」的逻辑。
try:
result = 10 / 2
except ZeroDivisionError:
print("除数不能为0")
else:
print("计算成功:", result) # 计算成功: 5.0
5. finally 子句
无论是否发生异常,finally 子句都会执行,通常用于释放资源(关闭文件、断开连接等)。
try:
f = open('file.txt', encoding='utf-8')
except FileNotFoundError:
print("文件不存在")
else:
print(f.read())
finally:
print("无论如何都会执行") # 总会输出
6. 主动抛出异常 raise
def set_age(age):
if age < 0:
# raise主动抛出异常,中断程序
raise ValueError("年龄不能为负数")
print("年龄设置成功:", age)
set_age(-1) # ValueError: 年龄不能为负数
7. 异常链 raise from
在 except 中抛出新异常时,使用 raise from 保留原始异常信息,方便排查问题根源。
def read_config():
try:
with open('config.json') as f:
return f.read()
except FileNotFoundError as err:
# 抛出更贴合业务的新异常,同时保留原始异常
raise RuntimeError("配置文件加载失败") from err
read_config()
# RuntimeError: 配置文件加载失败
# 上面一条 The above exception was the direct cause of... 会显示FileNotFoundError
8. 自定义异常
通过继承 Exception 类自定义异常,用异常名称本身表达业务错误含义。
# 自定义异常:继承Exception
class ConfigError(Exception):
"""配置相关的错误"""
pass
class NetworkError(Exception):
"""网络相关的错误"""
def __init__(self, message, status_code=None):
super().__init__(message)
self.status_code = status_code # 可以携带额外的错误信息
def load_config(path):
if not path.endswith('.json'):
raise ConfigError(f"不支持的配置格式:{path}")
try:
load_config('config.yaml')
except ConfigError as err:
print("配置错误:", err) # 配置错误: 不支持的配置格式:config.yaml
9. 实战:爬虫中的异常处理
爬虫请求网络时随时可能失败(超时、断网、接口改版),必须用异常处理兜底。以知乎热搜爬虫为例:
import requests
try:
response = requests.get("https://api.zhihu.com/topstory/hot-list", timeout=5)
response.raise_for_status() # 状态码非2xx时,主动抛出HTTPError
data = response.json() # 接口返回非JSON时,抛出JSONDecodeError
except requests.exceptions.Timeout:
print("请求超时,稍后重试")
except requests.exceptions.HTTPError as err:
print("服务返回错误状态码:", err)
except requests.exceptions.RequestException as err:
# RequestException是requests异常的基类,兜底捕获所有请求异常
print("请求失败:", err)
else:
print("请求成功,开始解析数据")
finally:
print("本次爬取结束")
常见内置异常速查
| 异常 | 触发场景 |
|---|---|
| ZeroDivisionError | 除数为 0 |
| NameError | 使用未定义的变量 |
| TypeError | 类型不符合操作要求,例如 "2" + 2 |
| ValueError | 类型正确但值不合法,例如 int("abc") |
| IndexError | 索引超出序列范围 |
| KeyError | 字典中不存在该键 |
| FileNotFoundError | 打开不存在的文件 |
| AttributeError | 对象没有该属性或方法 |
| KeyboardInterrupt | 用户按下 Ctrl+C 中断程序 |
| StopIteration | 迭代器没有下一个值 |
捕获顺序原则:子类异常在前、父类异常在后。except Exception 能兜住几乎所有异常,但会掩盖编程错误,只建议在最外层统一兜底记录日志时使用。
Loading...
