先講一個例子:
#老師有生日,怎么組合哪? class Birthday: # 生日def __init__(self,year,month,day):self.year = yearself.month = monthself.day = dayclass Teacher: # 老師<br>def __init__(self,name,birth):self.name = nameself.birthday = birthalex = Teacher('alex','2018-7-14') print(alex.birthday)# 2018-7-14#但是這么傳日期不好,需要分開,使用組合方式: class Birthday:def __init__(self,year,month,day):self.year = yearself.month = monthself.day = dayclass Teacher:def __init__(self,name,birth):self.name = nameself.birthday = birthbirth = Birthday(2018,7,14) alex = Teacher('alex',birth) # 用實例建立橋梁 print(birth.year) print(alex.birthday.year) # 調用組合對象中的屬性# 輸出結果 2018 2018 2018-7-14
Teacher 也可以定義一個方法,執行Birthday類里面的方法:
class Birthday:def __init__(self,year,month,day):self.year = yearself.month = monthself.day = daydef fmt(self):return '%s-%s-%s'%(self.year,self.month,self.day)class Teacher:def __init__(self,name,birth):self.name = nameself.birthday = birthdef birth_month(self): #算盤打到家了return self.birthday.fmt() # 引用組合對象的方法 birth = Birthday(2018,7,14) alex = Teacher('alex',birth) print(birth.year) print(alex.birthday.year) # 調用組合對象中的屬性 print(alex.birthday.fmt()) # 調用組合對象中的方法,要加括號 print(alex.birth_month())#運行結果: 2018 2018 2018-7-14 2018-7-14
總結: 組合就是一個對象引用另一個對象,用它的方法。
繼承的類:父類、基類、超類
想繼承的類:子類、派生類
?
?
講一個繼承的例子:
貓
屬性 性別 品種
方法 吃 喝 爬樹
狗
屬性 性別 品種
方法 吃 喝 看門
從上面可以看出,狗和貓有共同的屬性和方法,唯獨有一個方法是不一樣的。
那么是否可以繼承呢?
class Animal: # 動物def __init__(self,name,sex,kind):self.name = nameself.sex = sexself.kind = kinddef eat(self): # 吃print('%s is eating'%self.name)def drink(self): # 喝print('%s is drinking'%self.name)class Cat(Animal): # 貓def climb(self): # 爬樹print('%s is climbing'%self.name)class Dog(Animal): # 狗def watch_door(self): # 看門print('%s is watching door'%self.name)tom = Cat('tom','公','招財貓') # 實例化對象 hake = Dog('hake','公','藏獒') print(Cat.__dict__) # Cat.__dict__ Cat類的命名空間中的所有名字 print(tom.__dict__) # tom.__dict__ 對象的命名空間中的所有名字 tom.eat() # 先找自己對象的內存空間 再找類的空間 再找父類的空間 tom.climb() # 先找自己的內存空間 再找類的空間#運行結果: {'__doc__': None, 'climb': <function Cat.climb at 0x000001C95178AAE8>, '__module__': '__main__'} {'sex': '公', 'name': 'tom', 'kind': '招財貓'} tom is eating tom is climbing
??
?
一、Object類
class A:pass A()
#實例化的過程
? 1.創建一個空對象
? 2.調用init方法? 其實A是繼承的object,而其初識函數是空的
? 3.將初始化之后的對象返回調用處
以上代碼,A調用了init方法了?答案是:調用了! 這是什么調用,調用了object類的初始化函數
所有的類都繼承了object類
查看object的源碼,可以找到__init__方法:
def __init__(self): # known special case of object.__init__""" Initialize self. See help(type(self)) for accurate signature. """pass既然A繼承了object類,那么它肯定執行了父類object的__init__方法 加一段注釋class A:'''這是一個類'''passa = A() print(A.__dict__) # 雙下方法 魔術方法 #運行結果:{'__doc__': '\n 這是一個類\n ', '__module__': '__main__', '__dict__':
<attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__'
of 'A' objects>}
可以在上面的結果中找到兩個帶object的方法,很明顯這就是調用了object類!
總結:
任何類實例化都經歷3步,如果類沒有init,由object完成了。
?


class Parent:passclass Son(Parent):passprint(Son.__bases__) # (<class '__main__.Parent'>,)class Parent1:pass class Parent2:passclass Son(Parent1,Parent2):passprint(Son.__bases__) # (<class '__main__.Parent1'>, <class'__main__.Parent2'>)
?
二、單繼承:
比如人工大戰,人類和狗有共同屬性,比如名字,血量,攻擊了。還有共同的方法吃藥:
class Animal:def __init__(self,name,hp,ad):self.name = name # 名字self.hp = hp # 血量self.ad = ad # 攻擊力def eat(self):print('%s吃藥回血了' % self.name)class Person(Animal):def attack(self,dog): # 派生類print('%s攻擊了%s' %(self.name,dog.name))class Dog(Animal):def bite(self,person): # 派生類print('%s咬了%s' %(self.name,person.name))alex = Person('alex',100,10) print(alex.__dict__)#執行輸出:{'ad': 10, 'hp': 100, 'name': 'alex'}
但是還有不同的,比如人類有性別,狗類有品種怎么么辦哪?可以在子類init里面加屬#Person增加init方法
class Animal:def __init__(self,name,hp,ad):self.name = name # 名字self.hp = hp # 血量self.ad = ad # 攻擊力def eat(self):print('%s吃藥回血了' % self.name)class Person(Animal):def __init__(self,sex):self.sex = sexdef attack(self,dog): # 派生類print('%s攻擊了%s' %(self.name,dog.name))class Dog(Animal):def __init__(self,kind):self.kind = kinddef bite(self,person): # 派生類print('%s咬了%s' %(self.name,person.name)) # 人 sex alex = Person('alex') # 此時只能傳一個參數,否則報錯 print(alex.__dict__)
#執行輸出: {'sex': 'alex'}
發現和上面的例子少了一些屬性,animal繼承的屬性都沒有了。what?
因為子類自己有init方法了,它不會執行父類的init方法
那么如何執行父類的init呢?同時保證自己的init方法也能執行?
class Animal:def __init__(self,name,hp,ad):self.name = name # 名字self.hp = hp # 血量self.ad = ad # 攻擊力def eat(self):print('%s吃藥回血了' % self.name)class Person(Animal):def __init__(self,name,hp,ad,sex): Animal.__init__(self,name,hp,ad) # 執行父類方法 也可以使用super().__init__(name,hp,ad),它不需要self參數,
super(Person,self).__init__(name,hp,ad)self.sex = sexdef attack(self,dog): # 派生類print('%s攻擊了%s' %(self.name,dog.name))class Dog(Animal):def __init__(self,name,hp,ad,kind):Animal.__init__(self, name, hp, ad)self.kind = kinddef bite(self,person): # 派生類print('%s咬了%s' %(self.name,person.name))# 人 sex alex = Person('alex',100,10,'female') # 實例化 print(alex.__dict__)執行輸出: {'ad': 10, 'name': 'alex', 'hp': 100, 'sex': 'female'}
?
四、多繼承
?
多繼承和單繼承是一樣的
如果對象使用名字是子類中有的,那么一定用子類的
子類沒有,可以到多個父類中去尋找,問題來了,繼承誰的那?


class FlyAnimal:def fly(self):pass class SwimAnimal:def swim(self):passdef eat():pass class WalkAnimal:def walk(self):passdef eat():passclass Frog(SwimAnimal,WalkAnimal): pass class Tiger(SwimAnimal,WalkAnimal):pass class Swan(FlyAnimal,SwimAnimal,WalkAnimal):pass class Parrot(FlyAnimal,WalkAnimal):def talk(self):pass
?
?
?
?
三、super方法
Animal.__init__(self, name, hp, ad) 是直接使用類名.方法名 這樣執行的。
第二種寫法,使用super:
class Animal:def __init__(self,name,hp,ad):self.name = name # 名字self.hp = hp # 血量self.ad = ad # 攻擊力def eat(self): # 吃藥print('%s吃藥回血了' % self.name)<br> self.hp += 20class Person(Animal):def __init__(self,name,hp,ad,sex):#Animal.__init__(self,name,hp,ad) # 執行父類方法 # super(Person,self).__init__(name,hp,ad) # 完整寫法.在單繼承中,super負責找到
當前類所在的父類,在這個時候不需要再手動傳selfsuper().__init__(name, hp, ad) # 簡寫.效果同上。它不需要傳參數Person,self。
因為它本來就在類里面,自動獲取參數self.sex = sexdef attack(self,dog): # 派生類print('%s攻擊了%s' %(self.name,dog.name))class Dog(Animal):def __init__(self,name,hp,ad,kind):super().__init__(name, hp, ad)self.kind = kinddef bite(self,person): # 派生類print('%s咬了%s' %(self.name,person.name))# 人 sex alex = Person('alex',100,10,'female') # 實例化 print(alex.__dict__)#執行輸出: {'ad': 10, 'name': 'alex', 'hp': 100, 'sex': 'female'}類外層調用eat方法: #父類有eat,子類沒有 alex.eat() #找父類 執行輸出: alex吃藥回血了
比如人吃藥要扣錢,狗吃藥,不要錢。在Animal類中,eat方法,執行時,沒有扣錢。
那么就需要在人類中添加eat方法,定義扣錢動作。
class Animal:def __init__(self,name,hp,ad):self.name = name # 名字self.hp = hp # 血量self.ad = ad # 攻擊力def eat(self):print('%s吃藥回血了' % self.name)class Person(Animal):def __init__(self,name,hp,ad,sex):#Animal.__init__(self,name,hp,ad) # 執行父類方法# super(Person,self).__init__(name,hp,ad) # 完整寫法.在單繼承中,super負責找到當前類所在的父類,在這個時候不需要再手動傳selfsuper().__init__(name, hp, ad) # 簡寫.效果同上。它不需要傳參數Person,self。因為它本來就在類里面,自動獲取參數self.sex = sex # 性別self.money = 0 # 增加默認屬性moneydef attack(self,dog): # 派生類print('%s攻擊了%s' %(self.name,dog.name))def eat(self): # 重新定義eat方法super().eat() # 執行父類方法eatprint('eating in Person')self.money -= 50 # 扣錢 class Dog(Animal):def __init__(self,name,hp,ad,kind):super().__init__(name, hp, ad)self.kind = kinddef bite(self,person): # 派生類print('%s咬了%s' %(self.name,person.name))# 人 sex alex = Person('alex',100,10,'female') # 實例化 alex.eat() # 子類有eat 不管父類中有沒有,都執行子類的#執行結果: # 人 sex alex = Person('alex',100,10,'female') # 實例化 alex.eat() # 子類有eat 不管父類中有沒有,都執行子類的
# 總結:父類方法,如果子類有個性化需求,可以重新定義次方法。
在類外面
當子類中有,但是想要調父類的。
alex = Person('alex',100,10,'female') # 實例化 Animal.eat(alex) # 指名道姓 super(Person,alex).eat() # 效果同上,super(子類名,子類對象)方法,一般不用#執行輸出: alex吃藥回血了 alex吃藥回血了
一般不會在類外面,執行super方法。都是在類里面調用父類方法super是幫助尋找父類的在外部,super沒有簡寫。
?
?
四、鉆石繼承
父類是新式類,那么子類全是新式類,同理經典類也一樣。在python3里面沒有經典類。
多繼承:
這個形狀,像鉆石一樣,老外喜歡浪漫,有些書籍寫的叫鉆石繼承,代碼如下:
class A:def func(self):print('A') class B(A):def func(self):print('B') class C(A):def func(self):print('C') class D(B,C):def func(self):print('D')d = D() d.func()#執行輸出:D
#然而,把D的代碼注釋?
class D(B,C):
pass
#def func(self):
print('D')
#運行結果:B
#把B的代碼注釋:
class B(A):
pass
# def func(self):
print('B')
#執行輸出: C
#把C的代碼注釋:
class C(A):
pass
# def func(self):
print('C')
#執行輸出:A
?
?
?廣度優化 :


1.如果從左到右第一個類, 2.在后面的繼承順序中也是第一個,或者不再出現在后面的繼承順序中 3.那么就可以把這個點提取出來,作為繼承順序中的第一個類
?
廣度優先搜索算法(英語:Breadth-First-Search,縮寫為BFS),又譯作寬度優先搜索,或橫向優先搜索,是一種圖形搜索算法。簡單的說,
BFS是從根節點開始,沿著樹的寬度遍歷樹的節點。如果所有節點均被訪問,則算法中止。
也就是第一次不走到頭,頭可能被多個對象繼承,如A,從最后一個引用的類進入A
看圖,查看順序:
?
再看一個龜殼模型:
代碼如下:
class A:def func(self):print('A') class B(A):passdef func(self):print('B') class C(A):passdef func(self):print('C') class D(B):passdef func(self):print('D') class E(C):passdef func(self):print('E') class F(D,E):passdef func(self):print('F')f = F() f.func()
#執行輸出: F
?
看圖,查看順序:
# 在python2中還有深度優先,就是第一次會取到最深處,F->D->B->A->E->C。
在這個例子中,A為頂點,因為有2個類繼承了A
在執行第3步時,由于B繼承了A,B并沒有直接去找A。而是在這這一層中斷查找。
由同層的E去查找,然后到C,最后到A,這就是廣度優先算法
注意:在廣度優先算法中,Python直接提供了方法mro,可以查看搜索循環:
f = F() f.func() print(F.mro()) #需要注意的是super始終是遵循mro的#執行輸出:#通過mro可以查看順序 F [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
?#總結: 新式類 多繼承 尋找名字的順序,遵循廣度優先(mro)!
?經典面試題:
class A: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')d = D() d.func()#執行結果: 在D(B,C)中先看B中,再看C,從C到A A C B D
在上的例子中,super不是找父類的,它是找下一個節點的
遇到多繼承和super
? ? 對象.方法
? ? ? ? 找到這個對象對應的類
? ? ? ? 將這個類的所有父類都找到畫成一個圖
? ? ? ? 根據圖寫出廣度優先的順序
? ? ? ? 再看代碼,看代碼的時候,要根據廣度優先順序圖來找對應的super?
總結:super():
1、在單繼承中就是單純得尋找父類
2、在多繼承中就是根據子節點 通過mro循環尋找下一個類
?
深度優化? :只在Python2中,且沒有繼承object,沒有mro和super方法


一條路走到黑
?
? 所謂深度優化是“一路摸到黑”,也就是說深度優化搜索會不假思索地一直擴展一個狀態直到到達不能被擴展的葉子狀態。
要用到Python2測試,代碼如下:
class A:def func(self):print('A') class B(A):def func(self):print('B') class C(A):def func(self):print('C') class D(B,C):def func(self):print('D')d = D() d.func()#執行結果: D 在Python2里面,不手動繼承Object,比如class A(object)就是經典類,比如calss A,在例子中B繼承了A,B在去找A,執行輸出A
?
順序如圖:
?
深度優化,一條路走到黑,找不到,就回來找其他的
?
?總結:
經典類:在Python2.*版本才存在,且必須不繼承object
? 1、遍歷的時候遵循深度優化算法
? 2、沒有mro() 和 super()方法
新式類: 在Python2.*版本中,需要繼承object才是新式類
1、遍歷的時候遵循廣度優先算法
2、在新式類中,有mro方法,同時也有super方法,但是在2.*版本中,必須傳參數(子類名,子類對象)
?