2014-07-03 30 views
1

我最近在Python的with-statement语句中遇到了一个奇怪的行为。我有一个代码,它使用Python的上下文管理器来回滚__exit__方法中的配置更改。经理在__exit__的finally块中有return False值。我隔离在下面的代码的情况下 - 唯一的区别是return语句的缩进:在python上下文管理器中返回finally块

class Manager1(object): 

    def release(self): 
     pass # Implementation not important 

    def rollback(self): 
     # Rollback fails throwing an exception: 
     raise Exception("A failure") 

    def __enter__(self): 
     print "ENTER1" 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     print "EXIT1" 
     try: 
      self.rollback() 
     finally: 
      self.release() 
      return False   # The only difference here! 


class Manager2(object): 

    def release(self): 
     pass # Implementation not important 

    def rollback(self): 
     # Rollback fails throwing an exception: 
     raise Exception("A failure") 

    def __enter__(self): 
     print "ENTER2" 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     print "EXIT2" 
     try: 
      self.rollback() 
     finally: 
      self.release() 
     return False  # The only difference here! 

在回滚上面的代码中有一个异常失败的。我的问题是,为什么Manager1的行为不同于Manager2。在Manager1中的with-statement之外没有抛出异常,以及为什么它在Manager2退出时抛出。

with Manager1() as m:   
    pass     # The Exception is NOT thrown on exit here 


with Manager2() as m: 
    pass     # The Exception IS thrown on exit here 

根据documentation of __exit__

如果一个异常被提供,并且该方法希望抑制 异常(即,防止它被传播)时,它应该返回一个 真值。否则,在从此方法退出 时,异常将被正常处理。

在我在这两种情况下出口没有返回真实想法,因此异常不应该在这两种情况下supressed。然而在Manager1中是这样的。任何人都可以解释吗?

我使用Python 2.7.6。

+0

请参阅[return eats exception](http://stackoverflow.com/q/517060/222914) –

回答

3

如果finally子句被激活意味着要么try块已成功完成,或它提出已经处理的错误,或者说,try块执行的return

在Manager1中执行作为finally子句的一部分的return语句使其正常终止,返回False。在Manager2类中,finally子句仍然执行,但如果它是由于引发异常而执行的,它不会阻止该异常传播回调用链直到被捕获(或直到它终止您使用回溯编程)。

Manager2.__exit__()只会在没有引发异常的情况下返回False。

+0

我相信这会更清楚一个例子。像'def test():try:raise Exception();最后:返回True'并显示调用'test'不会引发任何异常。 – Bakuriu

+1

[在此文档中:](https://docs.python.org/2/reference/compound_stmts.html#the-try-statement)“如果finally子句引发另一个异常或执行返回或中断语句,则保存例外被丢弃“ –

+0

@Bakuiriu谢谢,你是正确的,我很乐意为你编辑答案。我现在有点被推了。 – holdenweb

1

我认为一个好的方式来理解,这是通过看一个单独的例子无关的所有上下文管理的东西:

>>> def test(): 
     try: 
      print('Before raise') 
      raise Exception() 
      print('After raise') 
     finally: 
      print('In finally') 
     print('Outside of try/finally') 

>>> test() 
Before raise 
In finally 
Traceback (most recent call last): 
    File "<pyshell#7>", line 1, in <module> 
    test() 
    File "<pyshell#6>", line 4, in test 
    raise Exception() 
Exception 

所以你可以看到,当一个例外是try块内抛出,在之前的任何代码,执行异常并执行finally块内的任何代码。除此之外,其他一切都会被跳过。这是因为正在抛出的异常结束了函数调用。但是由于异常出现在try块内,因此各自的finally块有最后的运行机会。

现在,如果您在函数中注释掉raise行,您将看到所有代码都被执行,因为该函数不会提前结束。

相关问题