2013-07-03 83 views
1

我有下面的類繼承模式:孩子的方法是運行而不是父母的?

object <- A <- B <- Z 

現在每個班都會有公共setup()方法,它調用其他的「私人」的方法_configure(),其中包含加載代碼,我不想擾亂setup()。重點是讓每個班級「知道」如何設置自己,並在其他執行之前完成。

所以我想什麼叫Z上的實例安裝的時候是在使用_configure每個設置在其自己的類定義爲運行 A的設置和B的設定(爲了現在是不是特別重要)。

現在下面的腳本

#!/usr/bin/python 

class A(object): 

    def __init__(self): 
     self.configured = [] 
     self.set_up = [] 

    def _configure(self): 
     self.configured.append("A") 

    def setup(self): 
     self._configure()  # calls B._configure()! 
     self.set_up.append("A") 

class B(A): 

    def _configure(self): 
     self.configured.append("B") 

    def setup(self): 
     super(B, self).setup() 
     self._configure() 
     self.set_up.append("B") 

class Z(B): 
    pass 

if __name__ == "__main__": 
    z = Z() 
    z.setup() 
    print "configured: %s" % z.configured 
    print "set up: %s" % z.set_up 

運行B._configure()兩次,因此返回

[email protected]:~$ ./t.py 
configured: ['B', 'B'] 
set up: ['A', 'B'] 
[email protected]:~$ 

,而不是configured: ['A', 'B']...

有人可以向我解釋這一點嗎?我應該如何確保A.setup調用A._configure

解決方法:什麼工作了,現在是更換self._configureA._configure(self),但似乎醜陋和非面向對象:現在每一個可能被潛在的繼承應該重複它的名字與每一個方法調用類? self的美麗和簡潔在哪裏?

+2

你的輸出說它調用了兩次'B._configure',而不是'A._configure'。 –

+0

這看起來像它的工作方式完全一樣 - 一般來說,子方法被調用,除非你使用'super'。 – Marcin

回答

1

最後我明白了,必要的魔法叫做name manglingname scrambling

該解決方案與在配置方法的名稱中使用兩個下劃線一樣簡單。當Python看到這樣有趣的命名方法(或其他屬性)時,它將靜默地重命名爲它到_A__configure,因此即使用戶(意外或不是)覆蓋它,也會調用正確的方法。

(到現在爲止,我認爲「兩個下劃線」保留爲Python內部,虛心避免他們所有的時間......)

固定碼:

#!/usr/bin/python 

class A(object): 

    def __init__(self): 
     self.configured = [] 
     self.set_up = [] 

    def __configure(self): 
     self.configured.append("A") 

    def setup(self): 
     self.__configure() 
     self.set_up.append("A") 

class B(A): 

    def __configure(self): 
     self.configured.append("B") 

    def setup(self): 
     super(B, self).setup() 
     self.__configure() 
     self.set_up.append("B") 

class Z(B): 
    pass 

if __name__ == "__main__": 
    z = Z() 
    z.setup() 
    print "configured: %s" % z.configured 
    print "set up: %s" % z.set_up 

返回所需

[email protected]:~$ ./t.py 
configured: ['A', 'B'] 
set up: ['A', 'B'] 
[email protected]:~$ 
+0

我給了你一個「up」,因爲當你不想在某些語言中使用已知的技術時,「沒有做多態」就像C++一樣(首先,不要聲明虛擬的東西,顯然python默認爲100%的方法),但是在其他方面,它不怎麼明顯。這是一個Python演示,因此值得讚賞。 –

3

B類的setup方法調用super,所以它調用從Asetup方法......這是你的方法執行的順序:

Z.setup() 
    B.setup() 
    A.setup (via super) 
     B._configure 
     B.configured.append("B") 
     B.setup.append("A") 
    B._configure 
     B.configured.append("B") 
    B.set_up.append("B") 

這意味着Z.setup呼叫通過繼承B.setup,其通過super調用A.setup,其調用B._configureB.setup再次呼叫B._configure並返回後append

希望這清楚地(或多或少)

UPDATE:這裏是發生了什麼事一點解釋:你要定義一個類Z它有一個公共方法setup和私人方法_configure。這些方法未在Z類中定義,但在其父類B中未定義。但是,由於繼承,您仍然可以撥打Z.setupZ._configure

現在,Z.setup方法使用B類中的定義來工作。這個調用super爲了調用A.setup中定義的代碼,而仍然是Z,所以當方法_configure被調用時,將使用哪個定義?因爲我們是Z(即selfZ的一個實例),所以將調用的方法將是Z._configure,因爲這就是繼承的做法。

再次查看代碼:您正在覆蓋B_configure方法中首先定義的A。由於在B._configure中沒有super的調用,因此如果對象是從A繼承的類的實例,則不會調用A._configure

TL; DR:不能從Z打電話A._configure如果B定義_configure不調用超。這是繼承。如果您要撥打A.configure,請不要覆蓋B中的方法或使用super

希望這會有所幫助! :)

+1

我想問題是爲什麼'A.setup'調用'B._configure'而不是'A._configure'。 –

+0

因爲'A.setup'中的'self'因爲使用'super'而是B] – mrcasals

+0

@mrcasals ...所以*我怎樣才能讓它調用'A._configure'? –

相關問題