发布于 2026-01-06 6 阅读
0

Python DEV 全球展示挑战赛(由 Mux 呈现):展示你的项目!

使用 Python 捕获每一个异常

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

说到异常处理,我们都知道,棘手的情况可以通过三思而后行来解决,而对于其他情况,简单的斜杠“ try/”except就能搞定。但是,如果你错过了异常会发生什么呢?如果你使用的是任何现代面向对象编程语言,那么你的程序很可能会在关闭前立即向标准错误输出(STDERR)打印一些回溯数据。仅此而已。

在生产环境中,这是不可接受的。失败绝不是一个选项。

那么,如果我告诉你还有另一种方法呢?如果你可以捕获一个你从未明确尝试捕获异常,并在程序崩溃之前优雅地处理它呢?

让我来介绍sys一下excepthook

sys.excepthook(type, value, tb)

解释器会在程序因未处理的异常而崩溃之前立即调用此函数。

那么,我们可以用这个做什么呢?我们可以创建一个接受相同参数的函数,并将其赋值给它,sys.excepthook这样对原函数的调用sys.excepthook实际上就是对我们自己函数的调用。

例子:
import sys

def my_exception_hook(type, value, tb):
    """
    Intended to be assigned to sys.exception as a hook.
    Gives programmer opportunity to do something useful with info from uncaught exceptions.

    Parameters
    type: Exception type
    value: Exception's value
    tb: Exception's traceback
    """
    error_msg = "An exception has been raised outside of a try/except!!!\n" \
                f"Type: {type}\n" \
                f"Value: {value}\n" \
                f"Traceback: {tb}"
    # We're just printing the error out for this example, 
    # but you should do something useful here!
    # Log it! Email your boss! Tell your cat!
    print(error_msg)

sys.excepthook = my_exception_hook

# Let's do something silly now
x = 10 + "10"
Enter fullscreen mode Exit fullscreen mode

我把这段代码保存到了一个名为“testfile.py”的文件中。我们运行一下看看会得到什么结果。

josh@debian:~/tmp$ python3 testfile.py
An exception has been raised outside of a try/except!!!
Type: <class 'TypeError'>
Value: unsupported operand type(s) for +: 'int' and 'str'
Traceback: <traceback object at 0x7fac25f231c8>
Enter fullscreen mode Exit fullscreen mode

这太好了。我们掌握了一些关于异常的详细信息,可以记录下来并/或提醒开发人员。我们不仅知道在没有 try/except 语句的情况下抛出了异常,而且还知道这是一个 TypeError,原因是+使用了 `Traceback` 函数来执行加法/字符串连接操作(取决于开发人员的意图)。话虽如此,如果能了解更多关于这个错误的信息就更好了,我们可以利用从回溯参数中提取的信息来了解更多信息tb

从回溯信息中提取详细信息

为了从回溯信息中提取信息,我们需要导入 Python 的 `getTraceback`traceback模块。导入完成后,我们将使用extract_tb()该模块的函数从回溯信息中提取信息。本文仅关注如何传递extract_tb()回溯信息,但您需要知道,可以使用 `--returns` 参数来限制返回的信息量limit

extract_tb()返回一个实例,traceback.StackSummary其中包含有关回溯的所有信息。幸运的是,此类有一个format()方法可以返回一个字符串列表,每个字符串代表堆栈中的一个帧。

现在,让我们重写上面的函数,通过从回溯对象(我们的tb参数)中提取详细信息,来获取有关异常的更多信息。

import sys
import traceback

def my_exception_hook(type, value, tb):
    """
    Intended to be assigned to sys.exception as a hook.
    Gives programmer opportunity to do something useful with info from uncaught exceptions.

    Parameters
    type: Exception type
    value: Exception's value
    tb: Exception's traceback
    """

    # NOTE: because format() is returning a list of string,
    # I'm going to join them into a single string, separating each with a new line
    traceback_details = '\n'.join(traceback.extract_tb(tb).format())

    error_msg = "An exception has been raised outside of a try/except!!!\n" \
                f"Type: {type}\n" \
                f"Value: {value}\n" \
                f"Traceback: {traceback_details}"
    print(error_msg)

sys.excepthook = my_exception_hook

# Let's do something silly now
x = 10 + "10"
Enter fullscreen mode Exit fullscreen mode

让我们运行一下,看看结果如何!

josh@debian:~/tmp$ python3 testfile.py
An exception has been raised outside of a try/except!!!
Type: <class 'TypeError'>
Value: unsupported operand type(s) for +: 'int' and 'str'
Traceback:   File "testfile.py", line 19, in <module>
    x = 10 + "10"
Enter fullscreen mode Exit fullscreen mode

太好了!我们可以看到发生异常的文件、发生异常的代码行,以及导致异常的错误逻辑!

我在这里并没有对异常数据进行太多处理,这只是一个例子。我建议您将此异常记录到某个地方。此外,请考虑为这类异常设置一个特殊的退出代码。

总而言之……

请不要误解我的意思——我并不是建议你用这种方法替换你现有的try/except代码块。这种方法只是我作为紧急备用方案的手段。如果我的代码最终出现在这里,我仍然认为是一种损失,但至少我可以通过这种方式了解问题所在,而不是让异常详情被任何监视程序(如果有的话)获取STDERR

文章来源:https://dev.to/joshuaschlichting/捕捉-every-single-exception-with-python-40o3