首页 > 解决方案 > 为什么 try-except-else 中的 else 内部的异常不会重新抛出?

问题描述

我正在使用try| except| else| finally在蟒蛇。如果里面的代码else抛出异常,我希望我的整个脚本失败(在执行之后finally)。我发现这没有发生。内部的异常else被抑制。为什么?

MWE

import requests
def foo():
    try:
        resp = requests.get("https://example.com")
        status = resp.status_code
        assert resp.status_code < 300, "Bad status code"
    except requests.exceptions.BaseHTTPError:
        status = None
    else:
        print("Starting else branch")

        # this should fail
        # because the body is not json
        print(f"Response body was {resp.json()}")

        print("Finishing else branch")
    finally:
        # save the result persistently,
        # regardless of if it was good or bad
        with open('log.txt', 'w') as f:
            f.write(str(status))
        return status

print(f"foo() gracefully returned {foo()}")

我在 python 3.6.9、3.9.5 和 3.8 中尝试过。

期望的行为:

resp.json()在 内部抛出simplejson.errors.JSONDecodeError异常else,然后被捕获,finally运行,但随后重新抛出异常else,因此整个脚本失败并带有.JSONDecodeError

stdout显示:

开始其他分支

然后抛出异常,并回溯到resp.json().

elsein try|的python 文档 except| else| finally说:

使用 else 子句比在 try 子句中添加额外的代码要好,因为它可以避免意外捕获不是由 try ... except 语句保护的代码引发的异常。

因此,听起来该else子句的目的是将代码放在您希望捕获异常的地方。这就是我想要的。但似乎else 正在捕获异常。

(请注意,在这个例子中,我希望 AssertionError 导致整个函数失败,并且 BaseHTTPError 被捕获并优雅地处理。这对于实际用例来说没有意义,但这只是一个简单的 MWE。)

实际行为

stdout说:

开始其他分支

foo() 优雅地返回 200

内部的JSONDecodeError异常else被捕获,finally被执行,代码返回200。也就是说,else代码中的异常被捕获了。else因此,分支的代码似乎受try.

如果我想JSONDecodeError为我的.json()线路被抓住,我会把它放在try. 如何使此脚本捕获JSONDecodeError错误?

标签: pythonerror-handlingtry-catch

解决方案


您所看到的已完全记录在案:

如果finally存在,它指定一个“清理”处理程序。该try子句被执行,包括任何exceptelse子句。如果任一子句发生异常且未处理,则暂时保存该异常。finally子句被执行。finally如果存在已保存的异常,则会在子句末尾重新引发。如果finally子句引发另一个异常,则将保存的异常设置为新异常的上下文。如果finally子句执行return, breakorcontinue语句,则丢弃保存的异常:

>>> def f():
...     try:
...         1/0
...     finally:
...         return 42
...
>>> f()
42

https://docs.python.org/3/reference/compound_stmts.html#try

要在执行else后引发内部异常finally,您不能有returninside finally。取消缩进以将其移出finally分支。


推荐阅读