2013-04-18 149 views
1

想不到更好的問題標題。隨意編輯。我有一個由許多類繼承的基類(它可能有更多的子類)。對於每個類,我有一系列我需要執行後期初始化的操作。該序列被封裝在功能runme()執行一系列對象方法調用部分初始化基類

class myBase(object): 
    def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     self.runme() 

    def runme(self): 
     self.preprocess() 
     self.evaluate() 
     self.postprocess() 

    def preprocess(self): 
     pass 

    def evaluate(self): 
     pass 

    def postprocess(self): 
     pass 

子類必須接受相同的屬性作爲鹼(和任何另外的屬性)。所有的人都會過乘坐三個功能 - preprocessevaluatepostprocess

class childA(myBase): 
    def __init__(self,neg,op,value,ad1): 
     super(childA,self).__init__(neg,op,value) 
     self.ad1 = ad1 
     #Must call runme() here again?? 
     runme() 

    def evaluate(): 
     #Something using self.ad1 
     blah = self.ad1+self.value 

我看到它的方式,它產生了一個問題 - childA調用基__init__第一,它調用runme(),這反過來將調用evaluate。由於孩子過度乘坐evaluate,執行的evaluate孩子的定義,但作爲self.ad1還沒有尚未被實例化,這將引發AttributeError

我可以從myBase刪除self.runme(),問題很可能會消失,但我可以進一步sublcass childAchildAA

class childAA(childA): 
    def __init__(self,neg,op,value,ad1): 
     super(childAA,self).__init__(neg,op,value,ad1) 
     self.runme() 

而且問題都可以重新顯現。因爲可以形成既childAchildAA的對象,我不能從childA的__init__刪除runme()(和需要處理)

目前,作爲一種解決方法,我不叫runme()__init__,而不是從呼叫叫它初始化後的程序。

obja=childA(foo,bar,baz,ad1) 
obja.runme() 

一個更簡單的方法是在孩子的__init__結束調用super(),但這並不出現是正確的

另一種方式是 - 告訴基類推遲調用邵仁枚的()到孩子班。這可能嗎?在我的基地說,我做

def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     if some_condition which checks if this is being called by a derived class: 
      self.runme() 

如果這些是最好的方法來解決它?或者,這是一個常見問題,其他建議的解決方案是什麼?

編輯

兩個答案被張貼(或刪除)的同意最好的辦法似乎是離開runme()調用基類,然後調用super()年底孩子的__init__

class myBase(object): 
    def __init__(self,neg,op,value): 
     self.neg = neg 
     self.op = op 
     self.value = value 
     #Process 
     self.runme() 

class childA(myBase): 
     def __init__(self,neg,op,value,ad1): 
      self.ad1 = ad1 
      super(childA,self).__init__(neg,op,value) 

在您需要依賴於現有值值的情況下,

class childA(myBase): 
    def __init__(self,neg,op,value,ad1): 
     self.ad1 = ad1 
     self.internal_value = self.value #Not yet initialized!! 
     super(childA,self).__init__(neg,op,value) 

這段代碼可以放在被調用第一runme()

def preprocess(self): 
    self.internal_value = value 
    #Rest of the stuff 
+1

請看http://rhettinger.wordpress.com/2011/05/26/super-considered-super/;我認爲如果'__init__'委託完成類的初始化,最後調用'super()'並不合邏輯。 – thkang 2013-04-18 07:02:35

+0

你爲什麼認爲「那不合適」?這正是您在這種情況下所需要的,這是最簡單的解決方案。我沒有看到任何理由使用非常複雜的代碼,只是因爲有人告訴你_easy_解決方案看起來不正確。 – 2013-04-18 07:37:34

+0

@ running.t是的你是對的。其實我原來的解決方案並沒有設想從子項中移除'runme()'調用。編輯了我的問題,包括我現在正在做的事情,因爲兩個人發佈了相同的答案,然後將其刪除 – RedBaron 2013-04-18 08:34:32

回答

0

如果兒童__init__需要部分初始化的對象進行了preprocess()或其他一些功能,在結束通話super()將無法​​正常工作確實。如果是這樣的話,你可以把runme形式__new__myBase

class myBase(object): 
    def __new__(cls, *args, **kwargs): 
     obj = super(myBase, cls).__new__(cls) 
     obj.__init__(*args, **kwargs) 
     obj.runme() 

    def __init__(self, a): 
     print 'myBase init' 
     self.list = ['myBase', a] 

    def runme(self): 
     print 'myBase:', self.list 

class ChildA(myBase): 
    def __init__(self, a, b): 
     print 'ChildA init' 
     super(ChildA, self).__init__(a) 
     self.list.extend(['ChildA', b]) 

    def runme(self): 
     print 'ChildA:', self.list 

class ChildAA(ChildA): 
    def __init__(self, a, b, c): 
     print 'ChildAA init' 
     super(ChildAA, self).__init__(a, b) 
     self.list.extend(['ChildAA', c]) 

    def runme(self): 
     print 'ChildAA:', self.list 

您可以訂購各種__init__函數中的代碼所要求的初始化過程,以及適當的runme函數總是後__init__完成被稱爲:

>>> ChildA(1, 2) 
ChildA init 
myBase init 
ChildA: ['myBase', 1, 'ChildA', 2] 
>>> ChildAA(1, 2, 3) 
ChildAA init 
ChildA init 
myBase init 
ChildAA: ['myBase', 1, 'ChildA', 2, 'ChildAA', 3]