python繼承簡介
Java只支持單繼承多接口模式。Python支持多繼承模式。
在多繼承模式中有個難題就是鑽石繼承
也叫做菱形繼承
。
下圖就是鑽石繼承
的例子。
一般來說,Leaf
類初始化時會初始化Medium1
類和Medium2
類,然後Medium1
類初始化時會初始化Base
類,Medium2
類初始化時也會初始化Base
類,所以這就導致Leaf
類初始化時對Base
類進行了兩次初始化操作。python是通過super()
來解決這個問題的。
還是首先來點乾貨,子類初始化時在__init__
方法裏如何調用父類的初始化方法。有如下兩種方法
父類名稱.__init__(self,參數1,參數2,...)
super(子類,self).__init__(參數1,參數2,....)
關於super()
我們可以通過help(super)
函數查看其使用說明。
super()
就等價於super(__class__, <first argument>)
,即super(當前class, self)
super(type, obj) -> bound super object; requires isinstance(obj, type)
,其中第一個參數是開始尋找父類的起始點(起始但不包括),第二個參數是需要一個對應第一個type的實例,即滿足isinstance(obj,type)
,這個方法將返回第一個滿足繼承關係的類,尋找順序遵從type.__mro__
屬性順序(mro
將類的樹形繼承關係變成了一個list)
下面展示一個例子,類之間的關係圖如下所示。
Student
類分別繼承於Person
類和IdCard
類,Person
類又繼承於Human
類,當然Human
類和IdCard
類都會繼承於object
類(這裏就不顯示出來了)。
可通過Student.mro()
查看Student
類的繼承關係(變成了list)。
特別注意,由於多繼承的緣故,__init__
方法的參數最好是寫成可選參數。
在Student
類的__init__
方法中super(Human, self).__init__(*args)
和IdCard.__init__(self, *args)
是等價的,這也可以從mro
列表順序看出來。Student
類的__init__
方法也說明了當子類繼承多個父類時,如果有需要的話,可以在子類的__init__
方法顯示調用多個父類的__init__
方法。
# -*- coding: UTF-8 -*-
class Human(object):
def __init__(self, sex):
print("Human init start")
self._sex = sex
print("Human init end")
def __str__(self):
return "Human sex is {}".format(self._sex)
class Person(Human):
def __init__(self, name, age, *args):
print("Person init start")
self._name = name
self._age = age
super().__init__(args[0])
print("Person init end")
class IdCard(object):
def __init__(self, *args):
print("IdCard init start")
self._id_card = args[-1]
print("IdCard init end")
class Student(Person, IdCard):
def __init__(self, grade, *args):
print("Student init start")
self._grade = grade
super().__init__(*args)
print(__class__)
# super(Human, self).__init__(*args) # 從Human開始起查找,但是不包括起點
IdCard.__init__(self, *args) # 這種方法也可以
print("Student init end")
def __str__(self):
s = "sex={}\tname={}\tage={}\tgrade={}\tidCard={}".format(self._sex, self._name, self._age, self._grade, self._id_card)
return s
# mro是method resolution order, 表示了類繼承體系中的成員解析順序
# 將繼承的樹關係變成了一個list
# [<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Human'>, <class '__main__.IdCard'>, <class 'object'>]
print(Student.mro())
s = Student(100, "patrick", 26, "male", "programmer")
print(s)
運行結果如下
在設計類時,類的繼承關係一定要設計好設計清楚,要不然真是個大坑,特別是多繼承條件下!!!
這裏需要再次說明的是,__init__
方法只是對實例對象做了屬性的初始化,並不等同於Java裏的new Student()
,在python裏是通過__new__
方法創建對象的,創建對象成功後再調用__init__
方法做屬性的初始化。
在創建對象時可以通過元類來定製化創建,動態地給對象添加屬性或方法,這裏就可以參考python面向對象編程中的元類使用。
參考網址
python如何解決鑽石繼承難題
python菜鳥教程----子類如何繼承父類初始化函數說明
python中關於子類調用父類初始化函數的說明