gpt4 book ai didi

python - 当前异常上下文掩盖了先前的错误

转载 作者:太空狗 更新时间:2023-10-29 18:22:12 25 4
gpt4 key购买 nike

以下是我在 Doug Hellman 网站上在名为“masking_exceptions_catch.py​​”的文件中找到的示例。我暂时找不到链接。 throws() 引发的异常被丢弃,而 cleanup() 引发的异常被报告。

在他的文章中,Doug 评论说处理是不直观的。在编写它时(大约 2009 年),我认为它是 Python 版本中的一个错误或限制,我在当前的 Mac 版 Python (2.7.6) 中运行了它。它仍然报告来自 cleanup() 的异常。我觉得这有点令人惊讶,并希望看到它实际上是如何正确或理想的行为的描述。

#!/usr/bin/env python

import sys
import traceback

def throws():
raise RuntimeError('error from throws')

def nested():
try:
throws()
except:
try:
cleanup()
except:
pass # ignore errors in cleanup
raise # we want to re-raise the original error

def cleanup():
raise RuntimeError('error from cleanup')

def main():
try:
nested()
return 0
except Exception, err:
traceback.print_exc()
return 1

if __name__ == '__main__':
sys.exit(main())

程序输出:
$ python masking_exceptions_catch.py
Traceback (most recent call last):
File "masking_exceptions_catch.py", line 24, in main
nested()
File "masking_exceptions_catch.py", line 14, in nested
cleanup()
File "masking_exceptions_catch.py", line 20, in cleanup
raise RuntimeError('error from cleanup')
RuntimeError: error from cleanup

最佳答案

回过头来回答。我先不回答你的问题。 :-)

这真的有效吗?

def f():
try:
raise Exception('bananas!')
except:
pass
raise

那么,以上有什么作用呢?提示危险音乐。

好吧,铅笔放下。
# python 3.3
4 except:
5 pass
----> 6 raise
7

RuntimeError: No active exception to reraise

# python 2.7
1 def f():
2 try:
----> 3 raise Exception('bananas!')
4 except:
5 pass

Exception: bananas!

嗯,这很有成效。为了好玩,让我们尝试命名异常。
def f():
try:
raise Exception('bananas!')
except Exception as e:
pass
raise e

现在怎么办?
# python 3.3
4 except Exception as e:
5 pass
----> 6 raise e
7

UnboundLocalError: local variable 'e' referenced before assignment

# python 2.7
4 except Exception as e:
5 pass
----> 6 raise e
7

Exception: bananas!

异常语义在 python 2 和 3 之间发生了相当大的变化。但是,如果在这里 python 2 的行为让你感到惊讶,请考虑:它基本上与 python 在其他地方所做的一致。
try:
1/0
except Exception as e:
x=4
#can I access `x` here after the exception block? How about `e`?
tryexcept不是范围。实际上,python 中很少有东西;我们有“LEGB 规则”来记住四个命名空间——本地、封闭、全局、内置。其他块根本不是作用域;我可以愉快地声明 xfor循环并期望在该循环之后仍然能够引用它。

所以,尴尬。异常是否应该被特殊情况限制在其封闭的词法块中? Python 2 说不,python 3 说 yes .但我在这里过于简单化了;裸 raise是您最初询问的问题,这些问题密切相关,但实际上并不相同。 Python 3 可能会强制命名异常的范围仅限于它们的块,而无需解决裸 raise事物。

什么裸raise做‽

常见用法是使用裸 raise作为保留堆栈跟踪的一种手段。 catch ,做记录/清理,再加注。很酷,我的清理代码没有出现在回溯中,99.9% 的时间有效。但是当我们尝试在异常处理程序中处理嵌套异常时,事情可能会变糟。有时。 (有关何时/不是问题,请参见底部的示例)

直观上,无参数 raise将正确处理嵌套的异常处理程序,并找出正确的“当前”异常以重新引发。但这并不完全是现实。原来 - 在这里进入实现细节 - 异常信息被保存为当前 frame object 的成员.在 python 2 中,根本没有管道来处理单个帧内堆栈上的推送/弹出异常处理程序;只是一个包含最后一个异常的字段,而不管我们可能对其进行了任何处理。这就是裸 raise抢。

6.9. The raise statement

raise_stmt ::= "raise" [expression ["," expression ["," expression]]]

If no expressions are present, raise re-raises the last exception that was active in the current scope.



所以,是的,这是 Python 2 中的一个问题,与回溯信息的存储方式有关 - 在 Highlander 传统中,只能有一个(回溯对象保存到给定的堆栈帧)。结果,裸 raise重新提出当前框架认为是“最后一个”异常的异常,这不一定是我们人类大脑认为特定于我们当时所处的词法嵌套异常块的异常。呸,瞄准镜!

那么,在python 3中修复了吗?

是的。如何? New bytecode instruction (两个,实际上,在 except 处理程序的开头还有另一个隐含的)但谁在乎 - 这一切都直观地“正常工作”。而不是得到 RuntimeError: error from cleanup ,您的示例代码引发 RuntimeError: error from throws正如预期的那样。

我不能给你一个官方的理由为什么这不包含在 python 2 中。这个问题已经知道 since PEP 344 ,其中提到了 Raymond Hettinger 在 2003 年提出的问题。如果我不得不猜测,修复这个是一个破坏性的变化(除其他外,它会影响 sys.exc_info 的语义),这通常是一个很好的理由不在一个小版本。

使用 python 2 时的选项:

1) 命名您打算重新提出的异常,并只处理添加到堆栈跟踪底部的一两行。你的例子 nested功能变为:
def nested():
try:
throws()
except BaseException as e:
try:
cleanup()
except:
pass
raise e

以及相关的回溯:
Traceback (most recent call last):
File "example", line 24, in main
nested()
File "example", line 17, in nested
raise e
RuntimeError: error from throws

所以,回溯被改变了,但它有效。

1.5) 使用 raise 的 3 参数版本.很多人不知道这个,它是一种合法(如果笨重)的方式来保留您的堆栈跟踪。
def nested():
try:
throws()
except:
e = sys.exc_info()
try:
cleanup()
except:
pass
raise e[0],e[1],e[2]
sys.exc_info给了我们一个包含 (type, value, traceback) 的 3 元组,这正是 raise 的 3 参数版本需要。请注意,此 3-arg 语法仅适用于 python 2。

2) 重构您的清理代码,使其不可能抛出未处理的异常。请记住,一切都是关于 范围 - 移动那个 try/exceptnested并转化为自己的功能。
def nested():
try:
throws()
except:
cleanup()
raise

def cleanup():
try:
cleanup_code_that_totally_could_raise_an_exception()
except:
pass

def cleanup_code_that_totally_could_raise_an_exception():
raise RuntimeError('error from cleanup')

现在您不必担心;因为异常从未到达 nested的范围,它不会干扰您打算重新加注的异常。

3) 裸用 raise就像你在阅读所有这些并接受它之前所做的那样;清理代码通常不会引发异常,对吗? :-)

关于python - 当前异常上下文掩盖了先前的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23707530/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com