day 24-1 繼承

亦雙弓發表於2019-01-08

繼承

什麼是繼承:
  繼承是一種建立新類的方式,在 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 的本質 :不是單純找父類 而是根據呼叫者的節點位置的廣度優先順序來的

相關文章