35

没有参数的 raise 声明的文档说

如果不存在表达式, raise 会重新引发在当前范围内活动的最后一个异常。

我曾经认为这意味着当前函数必须执行一个except子句。在阅读了这个问题并进行了一些实验之后,我认为这意味着堆栈上的任何函数都必须执行一个except子句,但我不确定。另外,我意识到我不知道堆栈跟踪如何与无参数加注一起工作:

def f():
  try:
    raise Exception
  except:
    g()

def g():
  raise

f()

生产

Traceback (most recent call last):
  File "foo", line 10, in <module>
    f()
  File "foo", line 5, in f
    g()
  File "foo", line 3, in f
    raise Exception
Exception

这看起来不像是最初加注时的筹码,也不像是再次加注时的筹码,或者两个筹码的串联,或者任何我能理解的东西。

我是否正确地寻找执行except子句的堆栈上的任何函数的无参数加注?此外,堆栈跟踪如何在 reraise 上工作?

4

3 回答 3

36

当您raise没有参数时,解释器会查找最后一个异常引发和处理。然后,它的行为与您使用raise最新的异常类型、值和回溯一样。

这存储在当前线程的解释器状态中,并且可以使用sys.exc_info(). 通过“处理”,我的意思是一个 except 子句捕获了异常。引用try声明文档

在一个 except 子句的套件被执行之前,关于异常的详细信息被分配给sys模块中的三个变量:sys.exc_type接收标识异常的对象;sys.exc_value接收异常的参数;sys.exc_traceback接收回溯对象(请参阅标准类型层次结构一节,标识程序中发生异常的点。这些详细信息也可通过sys.exc_info()返回元组的函数获得(exc_type, exc_value, exc_traceback)

请参阅Python 评估循环(C 代码)中的实现说明,特别是:

第二个要点是为了向后兼容:在捕获异常时调用一个函数并让该函数通过 sys.exc_ZZZ 访问捕获的异常是(现在也是)常见的。(例如:traceback.print_exc())。

回溯反映了你如何准确地进行再加注。它是当前堆栈(第 10 行调用f(),第 5 行调用g())加上引发异常的原始位置:第 3 行。

于 2013-08-01T18:29:09.117 回答
11

事实证明,Python 使用了一种令人惊讶的方式来构建回溯。Python 不是在创建异常时(如 Java )或在引发异常时(如我以前认为的那样)构建整个堆栈跟踪,而是在异常冒泡时一次一帧地构建部分回溯。

每当一个异常冒泡到一个新的堆栈帧时,以及当以单参数形式raise(或 Python 2 上的双参数形式)引发异常时,Python 字节码解释器循环就会执行PyTraceback_Here以添加一个新的前往表示堆栈跟踪的回溯对象的链接列表。(Python 2 上的 0-argumentraise和 3-argument raise,请跳过此步骤。)

Python 维护每个线程的异常堆栈(和回溯),这些异常(和回溯)由尚未完成执行的块except挂起。finally0 参数raise 恢复此堆栈上的顶部条目表示的异常(和回溯),即使exceptorfinally位于不同的函数中。

f执行它的raise

raise Exception

Python 构建一个与该行对应的回溯:

  File "foo", line 3, in f
    raise Exception

g执行 0-argument时raise,会恢复此回溯,但不会为 0-argumentraise行添加任何条目。

之后,当异常通过堆栈的其余部分冒泡时,g()f()调用的条目被添加到堆栈跟踪中,从而显示最终的堆栈跟踪:

Traceback (most recent call last):
  File "foo", line 10, in <module>
    f()
  File "foo", line 5, in f
    g()
  File "foo", line 3, in f
    raise Exception
Exception
于 2018-07-09T18:24:55.703 回答
-1

以下代码可能会帮助您了解 raise 关键字的工作原理:

def fun(n):
    try:
        return 0 / n
    except:
        print('an exception raised:')
        raise

try:
    fun(0)
except:
    print('cannot divide by zero')

try:
    fun('0')
except:
    print('cannot divide by a string')
于 2021-07-19T14:10:09.573 回答