2012-06-20 37 views
13

我正在努力與__getattr__。我有一個複雜的遞歸代碼庫,讓異常傳播很重要。Python:爲什麼__getattr__捕獲AttributeErrors?

class A(object): 
    @property 
    def a(self): 
     raise AttributeError('lala') 

    def __getattr__(self, name):  
     print('attr: ', name) 
     return 1  

print(A().a) 

結果:

('attr: ', 'a') 
1 

爲什麼這種行爲?爲什麼沒有拋出異常?此行爲未記錄(__getattr__ documentation)。 getattr()只能使用A.__dict__。有什麼想法嗎?

+2

我會說文檔的失敗在於它聲明'AttributeError'可以在'__get__'中引發(這就是你在這裏實際做的),但並不能解釋它具有什麼效果。它應該說'AttributeError'發信號給python,它應該像沒有找到屬性那樣工作(所以'__getattr__'會像往常一樣嘗試作爲備份) - 如果這不是你想要的行爲,那麼你應該提出其他的東西。 – James

回答

9

我只是改變了代碼

class A(object): 
    @property 
    def a(self): 
     print "trying property..." 
     raise AttributeError('lala') 
    def __getattr__(self, name):  
     print('attr: ', name) 
     return 1  

print(A().a) 

,正如我們看到的,確實是物業首次嘗試。但由於它聲稱不存在(通過提高AttributeError),__getattr__()被稱爲「最後的手段」。

它沒有明確記錄,但可以計算在「當屬性查找在常用位置未找到屬性時調用」。

+0

以及推薦的方法是什麼?不使用屬性或使用'__getattribute__'而不是'__getattr__'? –

+2

我會說沒有提高'AttributeError',或在__getattr__'中處理該案例,或者使用'__getattribute__'並調用'super(...).__ getattribute__',捕獲'AttributeError'。 – glglgl

+1

我無法理解爲什麼你想要在屬性中顯式地引用'AttributeError'。或者'__(get | set)attr [ibute] __'方法以外的任何地方,真的。 –

4

__getattr__在屬性訪問失敗且AttributeError失敗時調用。也許這就是爲什麼你認爲它'捕捉'錯誤。但是,它並不是,它是Python的屬性訪問功能,可以捕獲它們,然後調用__getattr__

__getattr__本身並沒有發現任何錯誤。如果在__getattr__中引發AttributeError,則會獲得無限遞歸。

6

__getattribute__ documentation說:

如果該類還定義__getattr__(),後者將不會被調用,除非__getattribute__()顯式調用它或將引發AttributeError

我讀到這(通過inclusio unius EST exclusio alterius)的話說,屬性訪問呼叫__getattr__如果object.__getattribute__(這是 「稱爲無條件實現屬性訪問」)恰好提高AttributeError - 無論是直接還是描述符__get__(例如屬性fget);請注意0​​應該「返回(計算)的屬性值或引起AttributeError例外」。

作爲一個類比,操作員特殊方法可以提高NotImplementedError,於是其他操作員方法(例如__radd____add__)將被嘗試。

6

使用__getattr__和屬性在同一個類中是危險的,因爲它可能會導致很難調試的錯誤。

如果屬性的獲取者拋出AttributeError,那麼將默認捕獲AttributeError,並調用__getattr__。通常,這會導致__getattr__在發生異常時失敗,但如果您非常不走運,則不會,並且您甚至無法輕鬆地將問題追溯到__getattr__

除非你的財產getter是微不足道的,否則你絕對不能100%確定它不會拋出AttributeError。例外情況可能會引發多個級別的深度。

這裏是你能做什麼:

  1. 避免使用屬性和在同一類__getattr__
  2. 添加try ... except塊的所有屬性獲取是不平凡
  3. 保持屬性獲取簡單,讓你知道他們不會拋出AttributeError
  4. 寫您自己版本的@property裝飾,它捕捉AttributeError並重新將其作爲RuntimeError投擲。

又見http://blog.devork.be/2011/06/using-getattr-and-property_17.html

編輯:萬一有人正在考慮的解決方案4(我不推薦),這是可以做到這樣的:

def property_(f): 
    def getter(*args, **kwargs): 
     try: 
      return f(*args, **kwargs) 
     except AttributeError as e: 
      raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2] 

    return property(getter) 

然後用@property_代替@property在覆蓋__getattr__的類中。

0

當你與__getattr__結合@property你反正註定:

class Paradise: 
    pass 

class Earth: 
    @property 
    def life(self): 
     print('Checking for paradise (just for fun)') 
     return Paradise.breasts 
    def __getattr__(self, item): 
     print("sorry! {} does not exist in Earth".format(item)) 

earth = Earth() 
try: 
    print('Life in earth: ' + str(earth.life)) 
except AttributeError as e: 
    print('Exception found!: ' + str(e)) 

提供了以下的輸出:

Checking for paradise (just for fun) 
sorry! life does not exist in Earth 
Life in earth: None 

當你真正的問題是用呼喚Paradise.breasts

__getattr__總是在AtributeError上升時調用。該例外的內容被忽略。

可悲的是,有沒有辦法解決給定hasattr(earth, 'life')將返回True(只是因爲__getattr__定義)這個問題,但仍將由屬性「生命」,因爲它不存在達成,而真正的根本問題是Paradise.breasts

我的部分解決方案涉及使用除@property塊以外的嘗試,這些塊已知受到AttributeError例外。

0

經常遇到這個問題,因爲我實施__getattr__很多,並有很多@property方法。這是我想出了一個裝飾以獲得更多有用的錯誤信息:

def replace_attribute_error_with_runtime_error(f): 
    @functools.wraps(f) 
    def wrapped(*args, **kwargs): 
     try: 
      return f(*args, **kwargs) 
     except AttributeError as e: 
      # logging.exception(e) 
      raise RuntimeError(
       '{} failed with an AttributeError: {}'.format(f.__name__, e) 
      ) 
    return wrapped 

而且使用這樣的:

class C(object): 

    def __getattr__(self, name): 
     ... 

    @property 
    @replace_attribute_error_with_runtime_error 
    def complicated_property(self): 
     ... 

    ... 

底層異常的錯誤消息將包括其實例的類的名稱提升了標的AttributeError。 如果你願意,你也可以登錄它。

+0

我實際上做了類似的工作,並將其稱爲'safe_property'。它做同樣的事情,只有一個裝飾者。 –

相關問題