我在 python 中使用 try
|except
|else
|finally
。
如果 else
中的代码抛出异常,我希望我的整个脚本失败(在执行 finally
之后)。
我发现这不会发生。 else
内的异常正在被抑制。为什么?
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
显示:
启动 else 分支
然后抛出异常,回溯到 resp.json()
。
The python docs 表示 else
|try
|except
|else
中的 finally
说:
使用 else 子句比在 try 子句中添加额外代码要好,因为它避免了意外捕获不是由 try ... except 语句保护的代码引发的异常。
所以听起来 else
子句的目的是将代码放在您不希望捕获异常的地方。这就是我想要的。但似乎 else
正在 中的异常被捕获。
(注意,在这个例子中,我希望 AssertionError 导致整个函数失败,并且 BaseHTTPError 被捕获并优雅地处理。这对于实际用例来说没有意义,但这只是一个简单的MWE。)
stdout
说:
启动 else 分支
foo() 优雅地返回 200
捕获JSONDecodeError
内部的else
异常,执行finally
,代码返回200
。也就是说,else
代码中的异常被捕获。所以看起来 else
分支中的代码是受 try
保护的。
如果我想让 JSONDecodeError
被我的 .json()
行捕获,我会把它放在 try
中。
我如何让这个脚本不捕捉到 JSONDecodeError
错误?
答案 0 :(得分:2)
您所看到的内容已完美记录:
<块引用>如果 finally
存在,它指定一个“清理”处理程序。执行 try
子句,包括任何 except
和 else
子句。如果在任何一个子句中发生异常并且没有被处理,则该异常被临时保存。 finally
子句被执行。如果有一个已保存的异常,它会在 finally
子句的末尾重新引发。如果 finally
子句引发另一个异常,则将保存的异常设置为新异常的上下文。如果 finally
子句执行 return
、break
或 continue
语句,则丢弃保存的异常:
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
https://docs.python.org/3/reference/compound_stmts.html#try
要在执行 else
后在 finally
内抛出异常,您不得在 return
内有 finally
。取消缩进以将其移出 finally
分支。