2011-07-03 58 views
1

我有一個三類繼承鏈:Baz繼承自繼承自Foo的Bar。我想在Foo類中定義一個方法,當它在子類中調用時,會返回其父類的輸出,並附加它自己的東西。這是所需的輸出:Python:超越最大遞歸深度,同時調用子類的超類方法

>>> foo = Foo() 
>>> bar = Bar() 
>>> baz = Baz() 
>>> print foo.get_defining_fields() 
['in Foo'] 
>>> print bar.get_defining_fields() 
['in Foo', 'in Bar'] 
>>> print baz.get_defining_fields() 
['in Foo', 'in Bar', 'in Baz'] 

的問題是,我誤解一些有關超類方法使用super()由子類調用,或一些其他子類的細節。該位工作正常:

>>> print foo.get_defining_fields() 
['in Foo'] 

bar.get_defining_fields()產生無限循環,運行本身,直到RuntimeError改爲引發呼喚foo.get_defining_fields和停止那裏,像我想它的。

這是代碼。

class Foo(object): 
    def _defining_fields(self): 
     return ['in Foo'] 

    def get_defining_fields(self): 
     if self.__class__ == Foo: 
      # Top of the chain, don't call super() 
      return self._defining_fields() 
     return super(self.__class__, self).get_defining_fields() + self._defining_fields() 

class Bar(Foo): 
    def _defining_fields(self): 
     return ['in Bar'] 

class Baz(Bar): 
    def _defining_fields(self): 
     return ['in Baz'] 

所以get_defining_fields在超類中定義的,在它的super()通話,試圖通過正確的子類名在每個子類中調用使用self.__class__。在Bar中調用時,它會解析爲super(Bar, self).get_defining_fields(),以便foo.get_defining_fields()返回的列表將被預置爲由far.get_defining_fields()返回的列表。

如果你正確理解Python的繼承機制和super()的內部工作原理,這可能是一個簡單的錯誤,但是因爲我顯然不這樣做,所以如果有人能指出我正確的方式來做這件事,我會很感激。


編輯:按照丹尼爾·羅斯曼的答案,我試圖用這種形式代替super()電話:return super(Foo, self).get_defining_fields() + self._defining_fields()

現在無限遞歸不再出現,但調用bar.get_defining_fields()當我得到一個不同的錯誤:

AttributeError: 'super' object has no attribute 'get_defining_fields' 

別的東西還是不對。


編輯:是的,終於想通了什麼,我在這裏失蹤。將丹尼爾的最新答案標記爲已接受的答案。

+1

@Charles蒂:這是真實的Python 3,所有類新的類,但在Python 2中不鼓勵。 – jena

+0

我刪除了我的評論,但之前我問過他爲什麼從對象派生。因此,耶納的答覆。 –

回答

6

的問題是在這裏:

return super(self.__class__, self)... 

self.__class__總是指當前具體類。所以在Bar中,它指的是Bar,而在Baz中,它指的是Baz。所以,酒吧調用父類的方法,但仍self.__class__指的酒吧,不富。所以你會得到無盡的遞歸。

這就是爲什麼你必須始終以超級引用類專門。就像這樣:

return super(Foo, self)... 

編輯權,因爲只有一流的定義get_defining_fields

你真的會以這種錯誤的方式去做。這根本不是super的工作。我想你可能會得到更好的效果,通過迭代self.__class__.__mro__,這是訪問超類的方法解析順序的方式:

class Foo(object): 
    def _defining_fields(self): 
     return ['in Foo'] 

    def get_defining_fields(self): 
     fields = [] 
     for cls in self.__class__.__mro__: 
      if hasattr(cls, '_defining_fields'): 
       fields.append(cls._defining_fields(self)) 
     return fields 

¬

+0

這解決了遞歸,但是卻帶來了一個AttributeError。我相應地編輯了這個問題。 –

+0

查看我的更新.. –

+0

是啊,我的想法很混亂,我覺得Bar會繼承get_defining_fields,並且在Bar下運行它會使它成爲「bar(方法)」,直到super()調用擔心。 –

相關問題