【发布时间】:2012-01-02 08:23:40
【问题描述】:
这是 Debian Squeeze 上的 Python 2.6.6(默认)。考虑以下 Python 代码。
import sys
try:
raise Exception("error in main")
pass
except:
exc_info = sys.exc_info()
finally:
try:
print "cleanup - always run"
raise Exception("error in cleanup")
except:
import traceback
print >> sys.stderr, "Error in cleanup"
traceback.print_exc()
if 'exc_info' in locals():
raise exc_info[0], exc_info[1], exc_info[2]
print "exited normally"
得到的错误是
Error in cleanup
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
Exception: error in cleanup
cleanup - always run
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
Exception: error in main
这个想法是为了应对某些代码或该代码的清理(始终运行)或两者都给出错误的情况。例如,Ian Bicking 在Re-raising Exceptions 中对此进行了一些讨论。在那篇文章的最后,(见Update:)他描述了如何处理代码+回滚/还原的类似情况(仅在出现错误的情况下运行)。
我摆弄了这个并想出了上面的代码,这有点怪异。特别是,如果 only 出现错误
清理(注释掉raise Exception("error in main")),代码仍然正常退出,尽管它确实打印出回溯。目前,我给予非清理错误优先级,所以它可以停止程序。
理想情况下,我希望任何一个错误都可以停止程序,但这似乎并不容易安排。 Python 似乎只想引发一个错误,如果有其他错误,则丢失其他错误,默认情况下它通常是最后一个错误。重新排列会产生类似上面的卷积。
另外locals() 的使用有点难看。可以做得更好吗?
编辑:srgerg's answer 向我介绍了上下文管理器和 with 关键字的概念。除了PEP 343,我发现的其他相关文档是(不分先后)。
Context Manager Types、The with statement 和 http://docs.python.org/reference/datamodel.html#context-managers。与以前的方法相比,这似乎是一个很大的改进,即涉及 try、excepts 和 finallys 的意大利面条代码。
总而言之,我希望这样的解决方案能给我两件事。
-
主代码或 清理以停止程序的运行。上下文管理器这样做, 因为如果 with 循环的主体有异常并且 exit 没有,则传播该异常。 如果 exit 抛出异常而 with 循环体没有, 然后传播。如果两者都抛出异常,则 exit 传播异常,while循环体中的异常是 压制。这一切都记录在案,即来自 Context Manager Types,
contextmanager.退出(exc_type、exc_val、exc_tb)
退出运行时上下文并返回一个布尔标志,指示是否应抑制发生的任何异常。 [...] 从此方法返回真值将导致 with 语句抑制异常并继续执行 紧跟 with 语句的语句。否则,此方法完成后异常继续传播 执行。执行此方法期间发生的异常将替换 with
正文中发生的任何异常 陈述。 [...] 传入的异常永远不应该被明确地重新引发。相反,这个方法应该返回一个假值 表明该方法成功完成并且不想抑制引发的异常。 如果两个地方都有异常,我想看回溯 来自两者,即使技术上只抛出一个异常。这是 true 基于实验,因为如果两者都抛出异常, 然后传播 exit 异常,但从主体回溯 仍然打印 while 循环,如 srgerg's answer。 但是,我找不到这个记录 任何地方,都不尽如人意。
【问题讨论】: