Python 在上下文管理器中捕获异常\uuuu输入
即使在Python 在上下文管理器中捕获异常\uuuu输入,python,exception,python-2.7,with-statement,contextmanager,Python,Exception,Python 2.7,With Statement,Contextmanager,即使在\uuuu enter\uuuu()中存在异常,是否可以确保调用\uuuu exit\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu >>> class TstContx(object): ... def __enter__(self): ... raise Exception('Oops in __enter__') ... ... def __exit__(self
\uuuu enter\uuuu()
中存在异常,是否可以确保调用\uuuu exit\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
>>> class TstContx(object):
... def __enter__(self):
... raise Exception('Oops in __enter__')
...
... def __exit__(self, e_typ, e_val, trcbak):
... print "This isn't running"
...
>>> with TstContx():
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __enter__
Exception: Oops in __enter__
>>>
事后看来,上下文管理器可能不是最佳的设计决策。否。如果在\uuuuu enter\uuuuuu()
中可能出现异常,则您需要自己捕获它,并调用包含清理代码的帮助器函数。如下所示:
import sys
class Context(object):
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
# Swallow exception if __exit__ returns a True value
if self.__exit__(*sys.exc_info()):
pass
else:
raise
def __exit__(self, e_typ, e_val, trcbak):
print "Now it's running"
with Context():
pass
要让程序在不执行上下文块的情况下继续运行,您需要检查上下文块内的上下文对象,并且只有在\uuuuuu enter\uuuu
成功时才执行重要的操作
class Context(object):
def __init__(self):
self.enter_ok = True
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
if self.__exit__(*sys.exc_info()):
self.enter_ok = False
else:
raise
return self
def __exit__(self, e_typ, e_val, trcbak):
print "Now this runs twice"
return True
with Context() as c:
if c.enter_ok:
print "Only runs if enter succeeded"
print "Execution continues"
据我所知,你不能完全跳过这个块。请注意,此上下文现在包含其中的所有异常。如果您不希望在\uuuuuuuuuuuuuuuuuuu
成功时吞下异常,请检查self。在\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu退出
中输入\uuuuuuuuuuuuuuu ok
并返回False
如果为True
则可以使用context
或者举一个例子:
参见Python上的contextlib2如果不需要继承或复杂的子例程,可以使用较短的方法:
from contextlib import contextmanager
@contextmanager
def test_cm():
try:
# dangerous code
yield
except Exception, err
pass # do something
我是这样做的。它以错误作为参数调用_exit__()。如果args[0]包含错误,它将在执行清理代码后重新运行异常。我建议您遵循RAII(资源获取是初始化)并使用上下文的构造函数来执行可能失败的分配。然后您的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
可以简单地返回self。如果构造函数失败,则在进入with上下文之前可能会引发异常
Foo类:
定义初始化(自):
打印(“初始化”)
引发异常(“booh”)
定义输入(自我):
打印(“输入”)
回归自我
定义退出(自身、exc类型、exc val、exc tb):
打印(“退出”)
返回错误
使用Foo()作为f:
打印(“带内”)
输出:
init
Traceback (most recent call last):
File "<input>", line 1, in <module>
...
raise Exception("booh")
Exception: booh
init
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
...
引发异常(“booh”)
例外:嘘
包含一个用于确保清理的示例:
如的文档中所述,如果实现中的后续步骤失败,此方法可用于清理已分配的资源
因此,您可以使用ExitStack()
作为TstContx()
上下文管理器的包装上下文管理器:
从contextlib导入ExitStack
使用ExitStack()作为堆栈:
ctx=TstContx()
stack.push(ctx)#现在离开'stack'可以确保调用'ctx.\uuuuu exit.\uuuuuuu'。
使用ctx:
stack.pop_all()#因为'ctx.\uu enter.\uuuu'没有提升,所以它可以自行处理清理。
... # 下面是实际上下文管理器的主体。
如果在\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>中出现异常,您调用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu退出
,有没有办法在客户端代码中突破的块?哈哈,我只是同时考虑了一下。我用同样的逻辑更新了我的问题。如果目标是执行退出,而不是执行包含内容的异常,那么您是否只需执行self.\uu exit\uu(*sys.exc\u info())
,然后raise
原始异常,而不考虑退出返回值?我遗漏了什么吗?是的,但这会在contextlib中抛出“生成器未屈服”。@thg435,合理,但我们可以尝试包装“屈服”。。。最后,finally在finally块中有很多变通方法,但问题的根源是不可能用
块跳过整个。因此,即使我们设法以某种方式在enter
中处理异常,块仍然会运行,以None
或一些其他垃圾作为参数。问题是,不可能从\uuuu enter\uuu
中跳过(请参见)+1:这个答案最适合我的用例,而且非常有意义。令人惊讶的是,它没有更多的选票。谢谢。从技术上讲,我没有回答这个问题;-)
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
from contextlib import contextmanager
@contextmanager
def test_cm():
try:
# dangerous code
yield
except Exception, err
pass # do something
class MyContext:
def __enter__(self):
try:
pass
# exception-raising code
except Exception as e:
self.__exit__(e)
def __exit__(self, *args):
# clean up code ...
if args[0]:
raise
init
Traceback (most recent call last):
File "<input>", line 1, in <module>
...
raise Exception("booh")
Exception: booh