繼承
什麼是繼承:
繼承是一種建立新類的方式,在 python 中,建立的類可以繼承一個或多個父類,父類又可稱為基類或超類,建立的類稱為派生類或子類
父類必須在子類上面
一個類 可以被多個類繼承
一個類 可以繼承多個父類 —— python 裡
class A:pass #父類 基類 超類
class B:pass
class A_son(A):pass #子類 派生類
class AB_son(A,B):pass
print(A_son.__bases__) #檢視 A_son 繼承了誰,檢視父類用的
print(AB_son.__bases__)
print(A.__bases__) #在 python 中任何一個沒有父類的類都是 object 類的兒子(類的祖宗)
print(B.__bases__) # python3 -新式類 #沒有繼承父類預設繼承 object
複制
單繼承 *****
人狗大戰
可以發現下面有共同屬性
#可以發現下面有共同屬性
class Person:
def __init__(self,name,hp,aggr,sex):
self.name = name
self.hp = hp
self.aggr = aggr
self.sex = sex
class Dog:
def __init__(self,name,hp,aggr,kind):
self.name = name
self.hp = hp
self.aggr = aggr
self.kind = kind
複制
接下來我們把重複代碼提出出來 —— 單繼承
class Animal:
def __init__(self,name,hp,aggr):
self.name = name
self.hp = hp
self.aggr = aggr
def eat(self):
print('吃藥回血')
self.hp += 100
class Person(Animal):
def __init__(self,name,hp,aggr,sex):
Animal.__init__(self,name,hp,aggr)
self.sex = sex #派生屬性
def attack(self,dog): #派生方法
dog.hp -= self.aggr
class Dog(Animal):
def __init__(self,name,hp,aggr,kind):
Animal.__init__(self,name,hp,aggr)
self.kind = kind #派生屬性
#假如說 dog 吃藥後,還想加點攻擊力
def eat(self): #派生方法
Animal.eat(self) #與調用父類中的方法相結合
self.aggr += 15
#然後執行個體化
per = Person('無極',200,30,'男')
dog = Dog('二哈',200,15,'Poodle')
#print(per.__dict__) #檢視繼承的屬性
#print(dog.__dict__)
per.attack(dog)
print(dog.hp)
dog.eat()
print(dog.hp,dog.aggr)
複制
派生
當然子類也可以添加自己新的屬性或者在自己這裡重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那麼調用新增的屬性時,就以自己的為準了
父類中沒有的屬性 在子類中出現 叫做派生屬性
父類中沒有的方法 在子類中出現 叫做派生方法
隻要是子類的對象調用,子類中有的名字 一定用子類的,子類中沒有才找父類的,如果父類也沒有報錯
如果父類 子類都有 用子類的
如果還想用父類的,單獨調用父類的:
父類名.方法名 需要自己傳 self 參數
super().方法名 不需要自己傳 self
正常的代碼中 單繼承 === 減少了代碼的重複
繼承表達的是一種 子類和父類的關系
super 的用法
隻在新式類中有,python3 中所有類都是新式類
在類中使用 不需要傳 self 參數,在類外使用必須要傳 類 和 對象
查找自己所在類的父類
class Animal:
def __init__(self,name,hp,aggr):
self.name = name
self.hp = hp
self.aggr = aggr
def eat(self):
print('吃藥加血')
self.hp += 100
class Person(Animal):
def __init__(self,name,hp,aggr,sex):
#Animal.__init__(self,name,hp,aggr)
super().__init__(name,hp,aggr) #不需要傳 self 了,對于單繼承 super 就可以找到父類了
self.sex = sex #派生屬性
per = Person('凱子',500,20,'男')
print(per.__dict__)
#也可以在類外用
#傳一個 類 和一個 對象
super(Person,per).eat()
print(per.hp) #原本是 500
複制
多繼承 ***
class A:
def func(self):
print("A")
class B:
def func(self):
print("B")
class C:
def func(self):
print("C")
class D(A,B,C): #調用是從左向右的順序
pass
#def func(self): #這裡注釋掉調用的就是 A
# print('D')
d = D()
d.func()
複制
鑽石繼承問題
遵循廣度優先(适用于多個繼承都可以找到 A 時) 然後在找深度
新式類中的繼承順序 : 廣度優先
class A:
def func(self):
print("A")
class B(A): #2.這裡也注釋掉調用的是 C
pass
#def func(self):
# print("B")
class C(A): #3.這裡也注釋掉調用的是 A
pass
#def func(self):
# print("C")
class D(B,C):
pass
#def func(self): #1.這裡注釋掉調用的是 B
# print('D')
d = D()
d.func()
print(D.mro()) #記錄了繼承關系
複制
super 的多繼承問題
super 的本質 :不是單純找父類 而是根據調用者的節點位置的廣度優先順序來的
super 調用順序是根據廣度優先的反向來調用的
class A(object):
def func(self): print('A')
class B(A):
def func(self):
super().func()
print('B')
class C(A):
def func(self):
super().func()
print('C')
class D(B,C):
def func(self):
super().func()
print('D')
b = D()
b.func()
複制
小結:
繼承 : 什麼是什麼的關系
單繼承 *****
先抽象再繼承,幾個類之間的相同代碼抽象出來,成為父類
子類自己沒有的名字,就可以使用父類的方法和屬性
如果子類自己有,一定是先用自己的
在類中使用 self 的時候,一定要看清楚 self 指向誰
多繼承 ***
新式類和經典類:
多繼承尋找名字的順序 : 新式類廣度優先,經典類深度優先
新式類中 有一個類名 .mro 方法,檢視廣度優先的繼承順序
python3 中有一個 super 方法,根據廣度優先的繼承順序查找上一個類
2.7
新式類 繼承 object 類的才是新式類 廣度優先
經典類 如果你直接建立一個類在 2.7 中就是經典類 深度優先
print(D.mro())
D.mro()
單繼承:子類有的用子類 子類沒有用父類
多繼承中,我們子類的對象調用一個方法,預設是就近原則,找的順序是什麼?
經典類中 深度優先
新式類中 廣度優先
python2.7 新式類和經典類共存,新式類要繼承 object
python3 隻有新式類,預設繼承 object
經典類和新式類還有一個差別 mro 方法隻在新式類中存在
super 隻在 python3 中存在
super 的本質 :不是單純找父類 而是根據調用者的節點位置的廣度優先順序來的