Python函数末尾的"raise"在"try"或"except"块外

6 投票
5 回答
11176 浏览
提问于 2025-04-19 11:22

如果raise不在tryexcept的代码块里,而只是作为函数的最后一条语句,它会发生什么呢?

def foo(self):
    try:
        # some code that raises an exception
    except Exception as e:
        pass

    # notice that the "raise" is outside
    raise

这个例子打印了1,但没有打印2,所以可以推测最后的raise语句只是重新抛出了最后一个发生的异常。

def foo():
    try:
        raise Exception()
    except Exception as e:
        pass

    print 1
    raise
    print 2

if __name__ == '__main__':
    foo()

有没有关于这种用法的官方文档呢?

5 个回答

1

我遇到过一个问题,想要在函数没有返回值的情况下,把之前捕获的错误再抛出来。于是我查了一下 systraceback 这两个模块,但没找到合适的方法,所以最后我选择在代码块外面存储这个错误。

def foo():
    caught = None

    try:
        raise Exception
    except Exception as e:
        caught = e
        pass

    raise caught


f = foo()

输出

Traceback (most recent call last):
  line 13, in <module>
  line 10, in foo
  line 5, in foo
Exception

虽然在上面的例子中这样做没什么用,但如果你需要在循环中尝试很多次并且重新抛出错误,这个方法就很有用了。我具体的需求是实现一个HTTP请求的重试机制。

import time

def foo(key):
    caught = None
    
    for i in [1, 2, 3, 4, 5]:
        try:
            return d[key]
        except KeyError as e:
            caught = e
            print(i)
            time.sleep(i)
            continue

    raise caught

d = {"bar": "baz"}

f = foo(key="baz")

输出

1
2
3
4
5
Traceback (most recent call last):
  line 19, in <module>
  line 15, in foo
  line 8, in foo
KeyError: 'baz'
2

根据这份文档,我们可以了解到:

如果没有提供任何表达式,使用raise会重新抛出当前作用域中最后一个活跃的异常。如果当前作用域没有活跃的异常,就会抛出一个TypeError异常,表示这是一个错误(如果在IDLE环境下运行,则会抛出Queue.Empty异常)。

这意味着,在你的代码中,如果在try ... except块中没有发生任何异常,那么你就强制程序抛出一个TypeError异常。

3

简单的说,使用“bare raise”会重新抛出当前的异常。通常在一个函数的最后使用它是没有意义的,除非这个函数是在处理异常的情况下被调用的:

单独使用“raise”是无效的,这时Python会抛出自己的异常。

>>> def x():
...     raise
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType

但是如果在异常处理块中调用它,它的表现就正常了。

>>> try:
...     int('a')
... except:
...     x()
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'a'
>>> 

编辑

如果这个函数是在尝试进行某种恢复操作,这样做可能是合理的。这个函数可以修复出错的地方,记录一条信息,触发灭火器等等……如果它仍然认为系统有问题,就可以使用raise

4

一个简单的 raise 语句会重新抛出最后捕获到的异常。你可以在这里了解更多信息:https://docs.python.org/2/tutorial/errors.html#raising-exceptions

7

正如Russell所说,

一个简单的raise语句会重新抛出最后捕获到的异常。

这不管是在try-except块里还是外面都没关系。如果之前捕获到了一个异常,那么调用raise就会重新抛出这个异常。否则,Python会报错,告诉你之前捕获的异常是None,并抛出一个TypeError,因为None并不是一个可以被抛出的东西。

正如tdelaney所说,除了在处理错误的函数中,这样做似乎没有什么意义。个人觉得,这种用法甚至不应该出现在错误处理函数里,因为raise应该仍然放在except块中。有人可能会试图用这个来执行代码,无论是否发生错误,但使用finally块才是正确的做法。另一种可能是用这个来判断在执行函数时是否发生了错误,但其实有更好的方法来做到这一点,比如返回一个额外的值来指示是否发生了错误或发生在哪里。

撰写回答