2014-07-03 31 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行,您將看到所有代碼都被執行,因爲該函數不會提前結束。

相關問題