3

使用super()和直接使用父類名是否有區別?例如:super()和Parent類名有什麼區別?

class Parent: 
    def __init__(self): 
     print("In parent") 
     self.__a=10 

class Child(Parent): 
    def __init__(self): 
     super().__init__()  # using super() 
     Parent.__init__(self) # using Parent class name 

c=Child() 

是否有內部super().__init__()Parent.__init__(self)之間的差異?

+0

'super()'使用** MRO **解決呼叫... ... –

+0

僅當您的子類有多個超類時,或者您可能在某些時候將您的子類更改爲繼承一個不同的超類。順便說一句,「父類」不是通常的繼承術語。 – khelwood

+0

@khelwood:嚴格地說,這不是真的:如果你繼承了一個具有多重繼承的類,你的'super()'實際上可以委託給一個slibing,而不是父類。它只是返回'__mro__'中的下一行。 –

回答

6

不在這種情況下。但在一般情況,特別是當你使用多重繼承super()委託給下一個對象在方法解析順序(MRO)作爲documentation指定:

super([type[, object-or-type]])

返回一個代理對象將方法調用委託給父代或 si金光閃閃的類。這對訪問在類中被覆蓋的繼承方法 非常有用。搜索順序與getattr()使用的 相同,只是跳過了類型本身。

類型的__mro__屬性列出了由兩個getattr()super()所使用的方法的分辨率搜索順序。屬性 是動態的,只要繼承層次更新爲 ,就可以更改。

(...)

(複製,黑體字加)

例如說你定義類,如(從this question, where the MRO is discussed in more detail借用):

class F: 
    def __init__(self): 
     print('F%s'%super().__init__) 
     super().__init__() 

class G: 
    def __init__(self): 
     print('G%s'%super().__init__) 
     super().__init__() 

class H: 
    def __init__(self): 
     print('H%s'%super().__init__) 
     super().__init__() 

class E(G,H): 
    def __init__(self): 
     print('E%s'%super().__init__) 
     super().__init__() 

class D(E,F): 
    def __init__(self): 
     print('D%s'%super().__init__) 
     super().__init__() 

class C(E,G): 
    def __init__(self): 
     print('C%s'%super().__init__) 
     super().__init__() 

class B(C,H): 
    def __init__(self): 
     print('B%s'%super().__init__) 
     super().__init__() 

class A(D,B,E): 
    def __init__(self): 
     print('A%s'%super().__init__) 
     super().__init__() 

隨後的A__mro__是:

A.__mro__ == (A,D,B,C,E,G,H,F,object) 

現在,如果我們撥打A(),它會打印:

A<bound method D.__init__ of <__main__.A object at 0x7efefd8645c0>> 
D<bound method B.__init__ of <__main__.A object at 0x7efefd8645c0>> 
B<bound method C.__init__ of <__main__.A object at 0x7efefd8645c0>> 
C<bound method E.__init__ of <__main__.A object at 0x7efefd8645c0>> 
E<bound method G.__init__ of <__main__.A object at 0x7efefd8645c0>> 
G<bound method H.__init__ of <__main__.A object at 0x7efefd8645c0>> 
H<bound method F.__init__ of <__main__.A object at 0x7efefd8645c0>> 
F<method-wrapper '__init__' of A object at 0x7efefd8645c0> 
<__main__.A object at 0x7efefd8645c0> 

因此它意味着在A上下文,並試圖時獲得__init__在於:

  • Asuper().__init__D.__init__;
  • super().__init__DB.__init__;
  • super().__init__BC.__init__;
  • super().__init__ of C is E.__init__;
  • super().__init__ of E is G.__init__;
  • super().__init__ of G is H.__init__;
  • super().__init__ of H is F.__init__;和
  • super().__init__Fobject.__init__

注意這樣說super()本身並不代表到父。例如Dsuper()BB不是D的超類,所以它確實是取決於對象類型(不在類上)。

現在的D情況下,__mro__是:

D.__mro__ = (D,E,G,H,F,object) 

如果我們構建一個D但是我們得到:

D<bound method E.__init__ of <__main__.D object at 0x7efefd864630>> 
E<bound method G.__init__ of <__main__.D object at 0x7efefd864630>> 
G<bound method H.__init__ of <__main__.D object at 0x7efefd864630>> 
H<bound method F.__init__ of <__main__.D object at 0x7efefd864630>> 
F<method-wrapper '__init__' of D object at 0x7efefd864630> 

所以D的背景下,認爲:

  • super().__init__DE.__init__;
  • super().__init__ of E is G.__init__;
  • super().__init__ of G is H.__init__;
  • super().__init__ of H is F.__init__;和
  • super().__init__Fobject.__init__

所以這裏Dsuper()導致E(爲__init__不是在A背景相同。

+1

我想你錯過了最重要的部分在那裏結束你的最後一句太快了:'A中的super()'將委託給'D','D'中的'super()'調用委託給'B',而如果你用' D'實例的'super()'調用將轉到'E'。 – Duncan

+0

@Duncan:完全沒有,請參閱最新的答案。 –

+1

是的,您的編輯與我的評論交叉。雖然我仍然認爲,對於直接D實例'super()'來說,它會有所不同, – Duncan

1
super().__init__(*args, **kwargs) 

感知你不通過「自我」 - 它會自動插入。

super()首次設計在Python 2至允許類被重用爲一個類層次混入的方式,他們的直接超類可能會改變:

讓我們在某個時間點supose你的代碼是這樣的:

class A: pass 
class B(A): 
    def __init__(self, *args, **kwargs): 
      ... 
      # Fixed call to A 
      A.__init__(self, *args, **kwargs) 

class C(A): 
    def __init__(self, *args, **kwargs): 
      ... 
      # Fixed call to A 
      A.__init__(self, *args, **kwargs) 

class D(C, B): 
    pass 

在這一點上,正確的OOP代碼應該執行C.__init__應鏈中的調用B.__init__:但是當超類別名稱是硬編碼不會發生 - A__init__總會來到我旁邊。 而且如果你在C中硬編碼B.__init__,那麼就會阻止C在沒有B的情況下工作,從而破壞了多重繼承的目的。

當您使用super()相反,Python的執行爲希望在類的__mro__屬性下一個父類中的方法搜索(MRO =方法解析順序。__mro__是連接到每個Python類的具體屬性)。 - 因此,如果在某個時間點D上面的類別不再繼承B,則在C中的呼叫將自動重新路由至A

還值得注意的是,在Python 3中引入了無參數形式的super以簡化其使用 - 在此之前,必須硬編碼對自己類的引用並在參數中插入self。這種形式是Python在編譯器中硬編碼的少數例外之一 - 當在方法體內看到super(或__class__)(即,它創建指向類本身的__class__變量)時,它在內部方法上改變內部方法其中super調用使用)

相關問題