子類繼承的初始化規則
首先需要說明關於類繼承方面的初始函數__init__()
:
- 如果子類沒有定義自己的初始化函數,父類的初始化函數會被默認調用,但是需要在實例化子類的對象時傳入父類初始化函數對應的參數
- 如果子類定義了自己的初始化函數,而在子類中沒有顯式調用父類的初始化函數,則父類的屬性不會被初始化,
- 如果子類定義了自己的初始化函數,在子類中顯示調用父類,子類和父類的屬性都會被初始化
對於情況1,如下:
class Base:
def __init__(self, name, id = 2):
self.name = name
self.id = id
print("Base create")
print("id = ", self.id)
def func(self):
print("base fun")
class childA(Base):
# def __init__(self):
# print("childA create")
# Base.__init__(self, "A") # 父類名硬編碼到子類中
def funA(self):
print("funA")
A = childA('john',id=2) # 必須手動傳入,否則A還是不會有name和id對象
print(A.name, A.id)
結果為:
Base create
id = 2
john 2
對於情況2,如下:
class Base:
def __init__(self, name, id = 2):
self.name = name
self.id = id
print("Base create")
print("id = ", self.id)
def func(self):
print("base fun")
class childA(Base):
def __init__(self):
print("childA create")
# Base.__init__(self, "A") # 父類名硬編碼到子類中
def funA(self):
print("funA")
A = childA()
print(A.name, A.id)
結果顯示為:
AttributeError: 'childA' object has no attribute 'name'
對於情況3,如下:
class Base:
def __init__(self, name, id = 2):
self.name = name
self.id = id
print("Base create")
print("id = ", self.id)
def func(self):
print("base fun")
class childA(Base):
def __init__(self):
print("childA create")
Base.__init__(self, "A") # 父類名硬編碼到子類中
def funA(self):
print("funA")
結果為:
Base create
id = 2
john 2
其中Base.__init__(self, "A")
就是朴素的子類調用父類的初始化,初始化時必須填入位置變量name
即這里的"A"
,而關鍵字變量id
可選。
super()
注意super()只能用在新式類中(當然用python3的人不用擔心這個問題),並且在單繼承類中super()跟單純的__init__()沒什么區別,如下:
class Base:
def __init__(self, name, id = 2):
self.name = name
self.id = id
print("Base create")
print("id = ", self.id)
def func(self):
print("base fun")
class childA(Base):
def __init__(self):
print("childA create")
Base.__init__(self, "A") # 父類名硬編碼到子類中
def funA(self):
print("funA")
class childB(Base):
def __init__(self):
print("childB create")
# super(childB, self).__init__('B') # super,將子類名和self傳遞進去
super().__init__('B',id=3) # python3可以直接簡化成這個形式
self.id = 3
另外需要注意的是super不是父類,而是繼承順序的下一個類,如下是多類繼承的情況:
class Base(object):
def __init__(self):
print 'Base create'
class childA(Base):
def __init__(self):
print 'enter A '
# Base.__init__(self)
super(childA, self).__init__()
print 'leave A'
class childB(Base):
def __init__(self):
print 'enter B '
# Base.__init__(self)
super(childB, self).__init__()
print 'leave B'
class childC(childA, childB):
pass
c = childC()
print c.__class__.__mro__
輸出結果如下:
enter A
enter B
Base create
leave B
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)
supder和父類沒有關聯,因此執行順序是A —> B—>—>Base,執行過程相當於:初始化childC()時,先會去調用childA的構造方法中的 super(childA, self).init(), super(childA, self)返回當前類的繼承順序中childA后的一個類childB;然后再執行childB().init(),這樣順序執行下去。
在多重繼承里,如果把childA()中的 super(childA, self).init() 換成Base.init(self),在執行時,繼承childA后就會直接跳到Base類里,而略過了childB:
enter A
Base create
leave A
(<class '__main__.childC'>, <class '__main__.childA'>, <class '__main__.childB'>, <class '__main__.Base'>, <type 'object'>)
super()復雜示例
下面舉一個更復雜的例子幫助更好的理解super():
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * self.length + 2 * self.width
class Square(Rectangle):
def __init__(self, length):
super(Square, self).__init__(length, length)
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
class RightPyramid(Triangle, Square):
def __init__(self, base, slant_height):
self.base = base
self.slant_height = slant_height
def area(self):
base_area = super().area()
perimeter = super().perimeter()
return 0.5 * perimeter * self.slant_height + base_area