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 能兜住几乎所有异常,但会掩盖编程错误,只建议在最外层统一兜底记录日志时使用。

上次编辑于:
贡献者: 15327360835
Loading...