为什么不重新抛出 try-except-else 中 else 内部的异常?
Why are exceptions inside else in try-except-else not re-thrown?
我在 python 中使用 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()
在 else
中抛出 simplejson.errors.JSONDecodeError
异常,然后被捕获,finally
是 运行,但是 [=13] 中的异常=] 被 重新抛出 ,因此整个脚本因 JSONDecodeError
.
而失败
stdout
显示:
starting else branch
然后抛出异常,回溯到resp.json()
.
The python docs 对于 try
|except
|else
|finally
中的 else
说:
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.
所以听起来 else
子句的目的是将代码放在您需要的地方 而不是 希望捕获异常。这就是我想要的。但似乎 else
中的异常被 捕获。
(请注意,在这个例子中,我希望 AssertionError 导致整个函数失败,并且 BaseHTTPError 被捕获并优雅地处理。这对于实际用例来说并没有多大意义,但这只是一个简单的 MWE。)
实际行为
stdout
说:
Starting else branch
foo() gracefully returned 200
捕获else
里面的JSONDecodeError
异常,执行finally
,代码returns200
。即捕获了else
代码中的异常。因此,else
分支 中的代码似乎受到 try
.
的保护
如果我想让 JSONDecodeError
被捕获到我的 .json()
行,我会把它放在 try
里面。
如何让此脚本 不 捕获 JSONDecodeError
错误?
您所看到的内容已得到完美记录:
If finally
is present, it specifies a ‘cleanup’ handler. The try
clause is executed, including any except
and else
clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally
clause is executed. If there is a saved exception it is re-raised at the end of the finally
clause. If the finally
clause raises another exception, the saved exception is set as the context of the new exception. If the finally
clause executes a return
, break
or continue
statement, the saved exception is discarded:
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
要在 finally
执行后在 else
内抛出异常,您不能在 finally
内有 return
。取消缩进以将其移出 finally
分支。
我在 python 中使用 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()
在 else
中抛出 simplejson.errors.JSONDecodeError
异常,然后被捕获,finally
是 运行,但是 [=13] 中的异常=] 被 重新抛出 ,因此整个脚本因 JSONDecodeError
.
stdout
显示:
starting else branch
然后抛出异常,回溯到resp.json()
.
The python docs 对于 try
|except
|else
|finally
中的 else
说:
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.
所以听起来 else
子句的目的是将代码放在您需要的地方 而不是 希望捕获异常。这就是我想要的。但似乎 else
中的异常被 捕获。
(请注意,在这个例子中,我希望 AssertionError 导致整个函数失败,并且 BaseHTTPError 被捕获并优雅地处理。这对于实际用例来说并没有多大意义,但这只是一个简单的 MWE。)
实际行为
stdout
说:
Starting else branch
foo() gracefully returned 200
捕获else
里面的JSONDecodeError
异常,执行finally
,代码returns200
。即捕获了else
代码中的异常。因此,else
分支 中的代码似乎受到 try
.
如果我想让 JSONDecodeError
被捕获到我的 .json()
行,我会把它放在 try
里面。
如何让此脚本 不 捕获 JSONDecodeError
错误?
您所看到的内容已得到完美记录:
If
finally
is present, it specifies a ‘cleanup’ handler. Thetry
clause is executed, including anyexcept
andelse
clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. Thefinally
clause is executed. If there is a saved exception it is re-raised at the end of thefinally
clause. If thefinally
clause raises another exception, the saved exception is set as the context of the new exception. If thefinally
clause executes areturn
,break
orcontinue
statement, the saved exception is discarded:>>> def f(): ... try: ... 1/0 ... finally: ... return 42 ... >>> f() 42
要在 finally
执行后在 else
内抛出异常,您不能在 finally
内有 return
。取消缩进以将其移出 finally
分支。