2016-01-22 67 views
2

嗯,我有包括(相關部分)類:__getattr __()的無限遞歸 - 但爲什麼它甚至被調用一次?

class satellite(object): 
    def __init__(self, orbit, payload=None, structural_mass=0, structural_price=0): 
     self.__all_parts = [] 
     self.orbit = orbit 
     self.payload = payload 
     self.structural_mass = structural_mass 
     self.structural_price = structural_price 

    def __getattr__(self, item): 
     found = False 
     v = 0 
     for i in self.__all_parts: 
      t = getattr(i, item, None) 
      if t is not None: 
       v += t 
       found = True 
     if found: 
      return v 
     else: 
      raise AttributeError(item) 

基本上我希望做的是傳播中的所有(總和)從屬性的「零件」到衛星。也就是說,如果我有10個有質量的部分,衛星的質量就是這些質量的總和。 - 如果我添加另一個具有能量存儲的部件 - 我立即可以看到它。 - 如果沒有零件具有該屬性,則該屬性被認爲是「壞」/「不存在」並且會引起正常錯誤。

現在這個工作,當我這樣做,除了:

s = satellite(None) #most simplistic one 
ss = copy.copy(s) 

整個事情蟲子,在__getattr__()給人一種無限遞歸深度誤差。

現在檢查(pycharm的調試器)讓我發現,它使迭代與作爲參數GETATTR:

item = _satellite__all_parts 它在該行for i in self.__all_parts:

現在,我通過這個嚇了一跳開始其下一個迭代:爲什麼這條線甚至會去__getattr_() - 據我所知__getattr__只被稱爲不存在的屬性的權利? - 但是self.__all_parts顯然是在__init__事件中聲明的對象,那麼爲什麼__getattr__甚至會被激活?此外:爲什麼它不再瞭解對象?

當然,我該怎麼做這項工作?

編輯:只是爲了清楚起見 - 這只是由於複製,而且它是(是感謝martijn)特定於複製行爲。鏈接的問題不處理複製案例。

+1

我不知道,但我期望它與雙下劃線前綴項的名稱修改有關。如果你使用'_all_parts'而不是它工作? –

+0

我不確定'__getattr__'實現是個好主意。如果屬性不能很好地添加?我不知道您的情況是否屬於這種情況,但可能存在以下特性:(a)無法添加,或(b)添加無意義(例如,衛星的速度不是總和它的部分速度)。 –

+0

@DanielRoseman:那麼修正直接調用它 - 但仍然使用「'copy.copy(t)'」給出相同的遞歸錯誤。 – paul23

回答

4

copy.copy()嘗試訪問的實例__setstate__屬性,設置還沒有屬性,因爲__init__尚未被稱爲(並不會; copy.copy()負責在其上設置新的屬性)。

__setstate__ methodcopy.copy()正在尋找是可選的,你的班級確實沒有這樣的屬性。由於缺少__getattr__,並且由於__all_parts屬性尚未或者,因此for i in self.__all_parts:行會再次調用__getattr__。一次又一次。

訣竅是儘早通過測試來切斷此循環。要做到這一點的最佳方式是特殊情況下以下劃線開始的所有屬性:

if item.startswith('_'): 
    # bail out early 
    raise AttributeError(item)