2015-08-24 88 views
3

看來Python處理AttributeError異常非標準。 當一個類定義了__getattr__方法時,它將吞噬此異常,而不是傳播到堆棧的頂端。原始異常是否丟失?AttributeError和丟失的異常消息

class A(object): 
    @property 
    def test(self): 
     raise AttributeError('message which should not be lost') 
     return 'this would never return' 

    def __getattr__(self, name): 
     print 'Trying get attribute: ', name 
     # how decide if AttributeError was already raised ?? 
     return 42 

a = A() 
print a.test 
# Trying get attribute: test 
# 42 

想象AttributeError異常可能在調用鏈中的任意深度的任何地方出現。

問題是如何保留與'message which should not be lost'消息的原始異常實例?有沒有辦法如何保持AttributeError而不求助於像替換不同的異常類這樣的解決方法?

+0

不要混合屬性,然後'__getattr__'。每當'__getattribute__'遇到一個'AttributeError'時'__getattr__'被調用。是的,這個例外很可能會丟失。 –

+0

@MartijnPieters不幸的是有時候這是無法避免的。例如,我正在使用基於Django的商店應用程序,並在Form子類中引入'__getattr__'。在form的驗證中,所有的屬性異常都是靜音的,它促使我發現錯誤來自哪裏。 – joanbm

回答

1

您正在通過提出AttributeError來給object.__getattribute__() handler一個表明屬性不存在的信號。定義的行爲是,然後調用__getattr__。例外情況丟失,由__getattribute__處理。從文檔:

無條件地調用來實現類的實例的屬性訪問。如果班級還定義了__getattr__(),則後者將不會被調用,除非__getattribute__()要麼明確調用它,要麼提出AttributeError

如果你不想做__getattribute__處理異常,您需要通過移動你的__getattr__行爲定製__getattribute__方法來代替:

class A(object): 
    @property 
    def test(self): 
     raise AttributeError('message which should not be lost') 
     return 'this would never return' 

    def __getattribute__(self, name): 
     try: 
      value = super(A, self).__getattribute__(name) 
     except AttributeError as ae: 
      # chance to handle the attribute differently 
      # if not, re-raise the exception 
      raise ae 

注意,hasattr() function的工作方式;當嘗試訪問屬性時會引發異常,它將返回False

+0

謝謝。爲了清楚說明,如果類定義了'__getattr__',則無論如何定義'__getattribute__',都沒有其他方法可以重新生成'AttributeError'來傳播異常。如果需要保存異常實例,需要在'__getattribute__'中執行? – joanbm

+0

@JoanBlackmoore:你必須完全重新實現'__getattribute__'(所以不要調用'super()'版本)來避免它吞噬異常,*或*你需要重命名'__getattr__'方法,然後調用從我已經顯示的'__getattribute__'覆蓋重命名的版本。 –

+0

再次感謝,聽起來有點hackish。順便說一句。在getattr方法中的任何地方調用'hasattr'似乎會導致無限遞歸。你可能意味着在屬性訪問的地方使用它,但在使用第三方代碼時幾乎是不可能的。 – joanbm