super() 是 Python 解決多重繼承方法調用順序的核心機制。本文深入剖析其運作原理、MRO(方法解析順序)、鑽石繼承問題與實務陷阱。
class A:
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
A.__init__(self) # 硬編碼父類別名稱
print("B done")
缺點:父類別變更需修改所有子類別,多重繼承時易導致重複調用。
class A(object): # 必須繼承 object(新式類別)
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
super(B, self).__init__() # Python 2 風格
# Python 3 可簡寫:super().__init__()
print("B done")
Python 使用 C3 線性化演算法 計算 MRO,確保:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (, , , , )
class Base:
def __init__(self):
print("Base init")
class Left(Base):
def __init__(self):
print("Left init")
Base.__init__(self) # 顯式呼叫
print("Left done")
class Right(Base):
def __init__(self):
print("Right init")
Base.__init__(self) # 顯式呼叫
print("Right done")
class Diamond(Left, Right):
def __init__(self):
print("Diamond init")
Left.__init__(self)
Right.__init__(self)
print("Diamond done")
# 執行 Diamond()
# 輸出:
# Diamond init
# Left init
# Base init <- 第一次
# Left done
# Right init
# Base init <- 第二次!重複調用
# Right done
# Diamond done
class Base(object):
def __init__(self):
print("Base init")
class Left(Base):
def __init__(self):
print("Left init")
super(Left, self).__init__()
print("Left done")
class Right(Base):
def __init__(self):
print("Right init")
super(Right, self).__init__()
print("Right done")
class Diamond(Left, Right):
def __init__(self):
print("Diamond init")
super(Diamond, self).__init__()
print("Diamond done")
# 執行 Diamond()
# 輸出:
# Diamond init
# Left init
# Right init
# Base init <- 僅調用一次!
# Right done
# Left done
# Diamond done
依 MRO 順序 Diamond → Left → Right → Base,每個 __init__ 僅執行一次。
# super 其實是一個類別
print(type(super)) #
print(super.__mro__) # (, )
# super(B, self) 建立一個代理物件
proxy = super(B, self)
print(type(proxy)) #
print(proxy.__self__) # self 實例
print(proxy.__thisclass__) # B 類別
super(B, self).__init__() 相當於:
B 的 MRO 列表B 之後的下一個類別(此例為 A)__init__,綁定 self
class Bad(Base):
def __init__(self):
super(Bad, self).__init__() # 呼叫 Base.__init__
Base.__init__(self) # 再呼叫一次!重複執行
class OldStyle: pass # 無繼承 object
# super(OldStyle, self) # TypeError: super() argument 1 must be type, not classobj
Python 3 所有類別隱式繼承 object,無此問題。
class A(object):
def __init__(self, x):
self.x = x
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x) # 傳遞 x 給 A
self.y = y
class C(A):
def __init__(self, x, z):
super(C, self).__init__(x)
self.z = z
class D(B, C):
def __init__(self, x, y, z):
# 必須確保參數相容,super() 只會呼叫 MRO 中下一個類別
super(D, self).__init__(x, y) # 呼叫 B.__init__(x, y)
self.z = z # C.__init__ 不會被自動呼叫!
解決:所有協作式方法簽章保持一致,或使用 **kwargs 透傳。
object(Python 3 自動)super(),不混用顯式呼叫__init__ 簽章保持一致,善用 *args, **kwargssuper(),更簡潔安全