2011-07-27 81 views
5

之一的方法,我有以下代碼:多重繼承和使用的基類

class A(object): 
    def __init__(self): 
     self.name = "A" 
     super(A, self).__init__() 

    def Update(self): 
     print "Update A" 
     self.PickTarget() 

    def PickTarget(self): 
     print "PickTarget A" 

class B(object): 
    def __init__(self): 
     self.name = "B" 
     super(B, self).__init__() 

    def Update(self): 
     print "Update B" 
     self.PickTarget() 

    def PickTarget(self): 
     print "PickTarget B" 

class C(A, B): 
    def __init__(self): 
     super(C, self).__init__() 

    def Update(self, useA): 
     if useA: 
      A.Update(self) 
     else: 
      B.Update(self) 

c = C() 

c.Update(useA = True) 
# prints: 
# Update A 
# PickTarget A 

c.Update(useA = False) 
# prints: 
# Update B 
# PickTarget A 

爲什麼叫C.UpdateuseA=False還叫成A.PickTarget?我如何使這個工作如何讓它工作(即B.Update總是調用B.PickTarget)?我確信之前已經提出過這個問題,但是我的搜索沒有任何結果 - 可能是因爲我不知道要搜索什麼。

+0

雖然我仍然認爲這可能需要重新設計,但我會指出,名稱修改爲此問題提供了相當靈活的解決方案。見[我的編輯](http://stackoverflow.com/questions/6849519/multiple-inheritance-and-using-a-method-of-one-of-the-base-classes/6849613#6849613)。 – senderle

回答

7

這是因爲AB之前C的基類。

您需要使用B.PickTarget(self)而不是self.PickTarget()B.Update(self)才能得到此行爲。否則,請在C的定義中切換AB

編輯:

如果預期的行爲是B總是調用方法BA總是調用方法A,這是正確使用A.method(self),而不是self.method(),作爲第二種形式沒有按」 t暗示methodA

你應該重新設計你的課程。 A應該有一個移動方法,隨機移動機器人並定義它的其他基本行爲。 B應該是A的一個子類,並且應該有一個移動方法,如果它沒有路徑,則會調用super(B, self).move(),否則將在路徑上移動。這是覆蓋條件方法的正確方法。

+0

如果不修改A和B,沒有辦法做到這一點嗎?這只是示例代碼 - 在實際代碼中,A和B也是獨立使用的。 (實際的代碼是機器人的一些簡單AI - A使機器人隨機移動,B使機器人沿着路徑前進,C檢查機器人是否有路徑 - 如果不是,則調用A.Update,如果是,則調用B 。更新。如果有更好的方法來處理這個問題,那麼我就全神貫注了。) – combatdave

+0

就像我說的,你可以在'C'的定義中切換'A'和'B'。另外,這聽起來像是你想要的行爲是'B'始終調用'B'中的方法,'A'始終調用'A'中的方法,在這種情況下,__correct__使用'A.method(self)'代替self.method()',因爲第二種形式並不意味着'method'在'A'中。 – agf

+0

我明白了!現在這開始有意義了。這是如何工作的訪問類中的屬性?例如,在A.Update中訪問self.name - 它應該是self.name還是A.name? – combatdave

2

無論何時調用對象的方法,python都會使用「方法解析順序」(MRO)來確定要調用哪個版本的方法。在這種情況下,雖然您明確地調用了A.Update(),A.Update()未明確調用A.PickTarget。它只是叫self.PickTarget()。由於這是一個C的對象,這相當於C.PickTarget(self)C.PickTarget()是繼承的,並且C MRO在這種情況下規定A.PickTarget是要使用的PickTarget的版本。

你可以看看C這樣的MRO:

>>> C.__mro__ 
(<class '__main__.C'>, <class 'foo.A'>, <class 'foo.B'>, <type 'object'>) 

有一個在MRO here超級翔實的文章。


關於如何得到你想要的行爲 - 嗯,有很多非常明顯的方式,並在同一時間,沒有那些(我能想到的)。我不認爲這是一個很好的設計。多重繼承的要點是你可以在C中混合和匹配正交方法,但是你試圖將多個版本的粗略類似的方法(A中的兩個和B中的兩個)塞入到一個類中。如果你告訴我們更多關於你在做什麼,也許我們可以提出更好的解決方案。 (你也改變了你的方法的簽名,同時保持相同的名字,這對我來說似乎也是狡猾的。)

但是,如果您確定要這樣做,您可能會考慮的另一種方法是name mangling,該方法專爲這種情況設計。這不正是你想要什麼:

class A(object): 
    def __init__(self): 
     self.name = "A" 
     super(A, self).__init__() 

    def Update(self): 
     print "Update A" 
     self.__PickTarget() 

    def PickTarget(self): 
     print "PickTarget A" 

    __PickTarget = PickTarget 

class B(object): 
    def __init__(self): 
     self.name = "B" 
     super(B, self).__init__() 

    def Update(self): 
     print "Update B" 
     self.__PickTarget() 

    def PickTarget(self): 
     print "PickTarget B" 

    __PickTarget = PickTarget 

class C(A, B): 
    def __init__(self): 
     super(C, self).__init__() 

    def Update(self, useA): 
     if useA: 
      A.Update(self) 
     else: 
      B.Update(self) 

輸出:

>>> from mangling import A, B, C 
>>> c = C() 
>>> c.Update(useA = True) 
Update A 
PickTarget A 
>>> c.Update(useA = False) 
Update B 
PickTarget B 
+0

感謝您爲何解釋這種情況。我已經嘗試將C.Update()中的self.PickTarget設置爲A.PickTarget或B.PickTarget,但是這會引發「TypeError:必須使用實例作爲第一個參數調用Unbound方法PickTarget()(取而代之)」 – combatdave

+0

@combatdave,我不太明白你的意思是「將C.Update()中的self.PickTarget設置爲A.PickTarget或B.PickTarget」。你的意思是你在做'self.PickTarget = A.PickTarget'?因爲那是超級錯誤。 'A.PickTarget'是一個未綁定的方法,所以現在它不再與'self'綁定了,你必須顯式地傳遞'self'。 – senderle

+0

是的,我的意思是說 - 我認爲它是錯誤的;)感謝帖子 - 這幫助我瞭解發生了什麼,但@agf的帖子解決了我的問題:) – combatdave

0

這不是解決辦法,但如果你改變C'S更新方法:

def update(self, useA): 
    if useA: 
     A().update() 
    else: 
     B().update() 

它的工作如你所料。

你應該閱讀關於Python的方法解析順序,以便正確地處理這樣的事情。

+1

這是創建一個基類的新實例調用Update(),這是沒有用的。 – combatdave

+0

然後嘗試在C的定義中將B切換爲A.編輯:另外,如果你嘗試打印C.name,你會看到發生了什麼。 – toqueteos