python报错跳过

Python 报错跳过(Error Skipping)完整教程#

目标:帮助你快速定位为什么代码会报错、如何在执行过程中“跳过”错误并继续往下跑,以及常见的坑与解决办法。

1. 引言#

在日常数据清洗、网络爬虫、并行计算等任务中,不想因为单个异常就导致整个程序崩溃。Python 的 try...except 机制让我们可以灵活地处理错误,但往往写得不够优雅、缺乏可维护性。本文从原因分析出发,给出 3 种分步骤的解决方案,并附带常见问题解答,助你在任何脚本中实现“报错跳过”。

说明:以下示例均使用 Python 3.10+,但大部分代码在 3.8/3.9 也能正常运行。


2. 原因分析#

场景 典型错误 产生原因 影响
文件读取 FileNotFoundError, PermissionError 路径错误、文件不存在、无读写权限 终止读取流程
网络请求 requests.exceptions.ConnectionError, Timeout 网络不稳定、服务器不可达 请求失败,后续流程中断
数据转换 ValueError, TypeError 字符串无法转为数值、类型不匹配 解析失败导致整个批次终止
数据库操作 sqlite3.IntegrityError, OperationalError 唯一约束冲突、连接中断 事务失败,后面依赖数据缺失

关键点:错误往往是可预期的 异常情况,不是完全不可处理。我们需要在代码层面判断、捕获并决定是否“跳过”。


3. 3 种分步骤的解决方案#

方案 A:基础 try...except + continue#

最常见、最直观。适用于循环内部的单个处理单元。

for item in data_list:
    try:
        process(item)          # 可能抛异常的逻辑
    except (ValueError, TypeError) as e:
        print(f"[WARN] 处理 {item} 失败: {e}")
        continue                # 跳过该条记录,继续下一条
    except Exception as e:
        # 捕获不可预期异常,记录后继续
        log.exception(f"不可预期错误: {e}")
        continue

优点:代码简洁,错误信息可直接打印/记录。
缺点:对异常类型需要手动列举,易漏。

步骤拆解#

  1. 包裹目标逻辑:将可能抛异常的代码放在 try 块内。
  2. 细化异常捕获:按业务需求列出具体异常;使用 Exception 捕获所有。
  3. 记录与跳过:在 except 内记录信息(printlogging),然后 continuepass

方案 B:使用 contextlib.suppress#

当你想抑制特定异常而不打断程序时,suppress 是最轻量的方案。

from contextlib import suppress

with suppress(FileNotFoundError, PermissionError):
    with open("config.json", "r") as f:
        config = json.load(f)
# 程序继续执行

优点:不需要手写 try...except,代码更简洁。
缺点:无法自定义日志,所有被抑制的异常会被“吞掉”,不易追踪。

步骤拆解#

  1. 导入 suppress
  2. 列出想抑制的异常
  3. 将可抛异常的代码放在 with suppress(...) 块中

适用于“如果文件不存在就用默认值,直接忽略”这类场景。


方案 C:自定义错误处理装饰器#

如果你在多处需要相同的错误跳过逻辑,写一个装饰器可以大幅提升可维护性。

import functools
import logging

def skip_errors(*exc_classes, log_level=logging.WARNING):
    """装饰器:捕获指定异常,记录日志后返回 None"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except exc_classes as e:
                logging.log(log_level, f"跳过 {func.__name__} 出错: {e}")
                return None
        return wrapper
    return decorator

# 用法
@skip_errors(ValueError, TypeError)
def parse_int(s: str):
    return int(s)

result = parse_int("abc")   # 记录警告,返回 None

优点:可复用且易维护,所有错误信息集中记录。
缺点:如果函数返回 None 与业务意义冲突,需要注意。

步骤拆解#

  1. 定义装饰器:接收要捕获的异常类型。
  2. 包装函数:使用 try...except 统一处理。
  3. 调用:在需要跳过错误的地方使用 @skip_errors

4. 常见问题解答(FAQ)#

问题 说明 解决方法
Q1. 为什么 except Exception 也会导致程序退出? 可能 except 里没有 continuereturn,导致后续代码不执行。 确认 except 内部有跳过逻辑(continue/pass)。
Q2. 如何在跳过错误时保留错误堆栈信息? 只打印错误会丢失堆栈。 使用 logging.exception()traceback.print_exc()
Q3. contextlib.suppress 也能记录错误吗? 默认不会记录。 自定义 suppress 或在 except 块中手动记录。
Q4. 在多线程/async 中怎么跳过错误? try...except 需要放在每个线程/协程里。 async def 内部使用 try...except,或者使用 asyncio.gather(..., return_exceptions=True)
Q5. 跳过错误后如何继续批量处理? 在循环里使用 continuereturn except 内部调用 continue 以跳过当前迭代,或 return 终止子函数。
Q6. 跳过错误会导致数据不完整,如何追踪缺失记录? 记录被跳过的索引或 ID。 在日志里输出 item_id,或者写入 failed_records.txt
Q7. 是否会出现“错误未被捕获”的情况? 如果 except 里没有列出所有可能的异常。 先写 except Exception 捕获所有,再细化需要的异常。
Q8. 如何在批量写入数据库时跳过冲突记录? INSERT OR IGNOREON CONFLICT DO NOTHING 在 SQL 语句中使用冲突处理,或者在 except sqlite3.IntegrityErrorcontinue

5. 小结#

  • 错误跳过 是一个 “容错” 的设计理念。
  • 选择合适的方案取决于 执行粒度(单条记录 vs 整个文件)与 错误类型(可预期 vs 随机)。
  • 始终记录错误,即使你在生产环境中不想让它阻塞业务。
  • 对于 大规模处理,建议使用 装饰器错误日志文件,避免在控制台打印过多信息。

实践建议:先在本地调试时使用 print,上线后改为 logging 并设置合适的日志级别;并在错误日志里记录 行号、异常类型上下文,方便后期排查。祝你写出既稳健又可维护的 Python 程序!