Fah*_*tha 5 python exception-handling
这是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"
Run Code Online (Sandbox Code Playgroud)
获得的错误是
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
Run Code Online (Sandbox Code Playgroud)
这个想法是为了应对某些代码或代码的清理(总是运行)或两者都给出错误的情况.例如,Ian Bicking在重新提出异常方面对此进行了一些讨论.在该帖子的最后,(参见参考资料Update:)他描述了如何处理代码+回滚/恢复的类似情况(仅在出错时运行).
我摆弄了这个并提出了上面的代码,这有点像怪物.特别是,如果清理中只有一个错误(注释掉raise Exception("error in main")),代码仍会正常退出,尽管它会打印出一个回溯.目前,我给出了非清除错误优先级,因此它可以停止程序.
理想情况下,我想要任何一个错误来停止程序,但这似乎不容易安排.Python似乎只想提出一个错误,如果有的话会丢失其他错误,默认情况下它通常是最后一个错误.重新排列会产生如上所述的卷积.
使用locals()也有点难看.一个人能做得更好吗?
编辑:srgerg的回答向我介绍了上下文管理器和with关键字的概念.除了PEP 343之外,我发现的其他相关文档(没有特别的顺序).
Context Manager Types,with语句和http://docs.python.org/reference/datamodel.html#context-managers.对于以前的方法来说,这似乎是一个很大的改进,即涉及trys,excepts和finallys的意大利面条代码.
总而言之,有两件事我想要这样的解决方案给我.
主代码或清理中的异常能够使程序停止运行.上下文管理器执行此操作,因为如果with循环的主体有异常而退出的主体没有异常,则传播该异常.如果exit抛出异常并且with循环的主体没有,则传播.如果两者都抛出异常,则 传播退出异常,并且抑制while循环体中的异常.这是所有记录的,即来自 Context Manager Types,
contextmanager.exit(exc_type,exc_val,exc_tb)
退出运行时上下文并返回一个布尔标志,指示是否应该抑制发生的任何异常.[...]从此方法返回true值将导致with语句抑制异常并继续执行紧跟在with语句之后的语句.否则,在此方法执行完毕后,异常将继续传播.执行此方法期间发生的异常将替换在with
语句的正文中发生的任何异常.[...]传递的异常不应该明确地重新加注.相反,此方法应返回false值以指示方法已成功完成,并且不希望抑制引发的异常.
如果两个地方都有异常,我希望看到两者都有回溯,即使技术上只抛出一个异常.这是基于实验的,因为如果两者都抛出异常,那么传播退出异常,但是仍然打印来自while循环体的回溯,如 srgerg的答案.但是,我无法在任何地方找到这个记录,这是不能令人满意的.
理想情况下,您可以使用 python with 语句来处理块内的清理try ... except,如下所示:
class Something(object):
def __enter__(self):
print "Entering"
def __exit__(self, t, v, tr):
print "cleanup - always runs"
raise Exception("Exception occurred during __exit__")
try:
with Something() as something:
raise Exception("Exception occurred!")
except Exception, e:
print e
import traceback
traceback.print_exc(e)
print "Exited normally!"
Run Code Online (Sandbox Code Playgroud)
当我运行它时,它打印:
Entering
cleanup - always runs
Exception occurred during __exit__
Traceback (most recent call last):
File "s3.py", line 11, in <module>
raise Exception("Exception occurred!")
File "s3.py", line 7, in __exit__
raise Exception("Exception occurred during __exit__")
Exception: Exception occurred during __exit__
Exited normally!
Run Code Online (Sandbox Code Playgroud)
注意,任何一个异常都会终止程序,并且可以在语句中处理except。
编辑:根据上面链接的 with 语句文档,__exit__()如果内部存在错误,该方法应该只引发异常__exit__()- 也就是说,它不应该重新引发传递给它的异常。
with如果语句中的代码和__exit__()方法都引发异常,就会出现问题。在这种情况下, except 子句中捕获的异常是 中引发的异常__exit__()。如果你想要 with 语句中提出的,你可以这样做:
class Something(object):
def __enter__(self):
print "Entering"
def __exit__(self, t, v, tr):
print "cleanup - always runs"
try:
raise Exception("Exception occurred during __exit__")
except Exception, e:
if (t, v, tr) != (None, None, None):
# __exit__ called with an existing exception
return False
else:
# __exit__ called with NO existing exception
raise
try:
with Something() as something:
raise Exception("Exception occurred!")
pass
except Exception, e:
print e
traceback.print_exc(e)
raise
print "Exited normally!"
Run Code Online (Sandbox Code Playgroud)
这打印:
Entering
cleanup - always runs
Exception occurred!
Traceback (most recent call last):
File "s2.py", line 22, in <module>
raise Exception("Exception occurred!")
Exception: Exception occurred!
Traceback (most recent call last):
File "s2.py", line 22, in <module>
raise Exception("Exception occurred!")
Exception: Exception occurred!
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1497 次 |
| 最近记录: |