2016-09-14 159 views
1

我有四個不同的類。有一個主基類/父類,從這個父類繼承的兩個主類,以及從這兩個主類繼承的另一個類。如果我有一個與父類相同的名稱但參數個數不同的方法,我會得到一個TypeError。具有相同方法名稱但不同參數的多繼承創建TypeError

# Example 

class Parent(object): 
    def check(self, arg): 
     tmp = { 
      'one': False, 
      'two': False 
     } 

     try: 
      if 'one' in arg: 
       tmp['one'] = True 

      if 'two' in arg: 
       tmp['two'] = True 
     except TypeError: 
      pass 

     return tmp 

class Child(Parent): 
    def check(self, arg): 
     return Parent.check(self, arg)['one'] 

    def method(self, arg): 
     if self.check(arg): 
      print 'One!' 

class ChildTwo(Parent): 
    def check(self, arg): 
     return Parent.check(self, arg)['two'] 

    def method(self, arg): 
     if self.check(arg): 
      print 'Two!' 

class ChildThree(Child, ChildTwo): 
    def check(self, arg, arg2): 
     print arg2 
     return Child.check(self, arg) 

    def method(self, arg): 
     if self.check(arg, 'test'): 
      print 'One!' 

     ChildTwo.method(self, arg) 

test = ChildThree() 
test = test.method('one and two') 

runfile('untitled6.py', wdir='./Documents')
test
One!
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
File "C:\Users\py\AppData\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 714, in runfile
execfile(filename, namespace)
File "C:\Users\py\AppData\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 74, in execfile
exec(compile(scripttext, filename, 'exec'), glob, loc)
File "untitled6.py", line 49, in
test = test.method('one and two')
File "untitled6.py", line 46, in method
ChildTwo.method(self, arg)
File "untitled6.py", line 34, in method
if self.check(arg):

TypeError: check() takes exactly 3 arguments (2 given)

然而,當我刪除從 'ChildThree' 的 '檢查' 方法的第二個參數,它似乎很好地工作:

class ChildThree(Child, ChildTwo): 
    def check(self, arg): 
     return Child.check(self, arg) 

    def method(self, arg): 
     if self.check(arg): 
      print 'One!' 

     ChildTwo.method(self, arg) 

runfile('untitled6.py', wdir='./Documents')
One!
Two!

我是相當新的類/繼承,所以我不知道爲什麼一個額外的參數會導致TypeError,即使它調用具有單個參數的父類方法。

回答

1

考慮這條線:

ChildTwo.method(self, arg) 

您在self傳遞明確。 self這裏是對ChildThree實例的引用。後來,在ChildTwo.method正文:

if self.check(arg): 

這是我們在這裏討論的相同self; self仍然是您的ChildThree實例的參考。

它看起來像你期望self做神奇的事情,但它不 - 它只是一個普通的舊名稱。要引用ChildTwo實例,它必須被稱爲綁定方法。比較和對比:

  • my_child_two.method(arg) < - 「自我」被通過描述協議隱式傳遞
  • ChildTwo.method(self, arg) < - 「自我」只是不管它是
+0

啊,這很有道理!一旦允許我(等待3分鐘),我會接受答案。有沒有一種好的/ Pythonic的方法來解決這個問題,或者我只是將'ChildTwo.method'主體中的'self.check(arg):'改爲'如果ChildTwo.check(self,arg)'? – PyNoob

+0

我建議的方式是避免在繼承情況下使用相同名稱和不同參數規範的方法。這聽起來不必要的複雜,「pythonic」的很大一部分就是簡單。 – wim

1

這種類型的繼承被稱爲"The Diamond Problem"。這是其自身的話題,所以我會在一個簡單的情況說明:

class C1(object): 
    def check(self, arg): 
     return 1 

    def method(self, arg): 
     return self.check(arg) 

class C2(C1): 
    def check(self, arg1, arg2): # this overrides C1.check! 
     return x + C1.check(self, arg1) 

c2 = C2() 
c2.method(55) # fails 

C2.check覆蓋C1.check所有C2實例。因此,當從method調用self.check(arg)時,它會針對C2的實例調用C2.check。這將失敗,因爲C2.check有兩個參數。

如何解決?當重寫方法時,不要更改它們的簽名(接收參數的數量和類型以及返回值的類型),否則會遇到麻煩。

[更高級]你可以有更多的自由功能,需要*args and **kwargs


除此之外,我看到ChildThree.check電話Child.check這就要求Parent.check,但沒有人叫ChildTwo.check。這是不對的。 您應該調用所有基類的方法(並且有可能調用父實現兩次,這可能就在這裏),或者使用super()

相關問題