面向對象三大特性
- 封裝 根據職責將屬性和方法封裝到一個抽象的類中;
- 繼承 實現代碼的重用,西安通的代碼不需要重復的編寫;
- 多態 不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度。
單繼承
使用繼承前的代碼
class Animal:
def eat(self):
print("吃")
def sleep(self):
print("睡")
class Dog:
def eat(self):
print("吃")
def sleep(self):
print("睡")
def bark(self):
print("犬吠")
dog = Animal()
dog.eat()
dog.sleep()
jinmao = Dog()
jinmao.eat()
jinmao.bark()
雖然可以通過代碼復制來減少工作量,但代碼重復卻很多。
繼承的概念和語法
繼承的概念:子類擁有父類所有的屬性和方法;
繼承的語法
class 類名(父類名):
pass
- 子類繼承自父類,可以直接享用父類中已經開發好的方法,不需要再次開發;
- 子類中應該根據職責,封裝子類中特有的屬性和方法;
單繼承示例
class Animal:
def eat(self):
print("吃")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("犬吠")
jinmao = Dog()
jinmao.eat() # 吃
jinmao.bark() # 叫
繼承相關術語
子類=派生類;
父類=基類;
繼承=派生;
例如:
Dog類是Animal類的子類,Animal類是Dog類的父類,Dog類從Animal類繼承;
繼承的傳遞性
- C類繼承自B類,B類又繼承自A類;
- 那么C類就擁有B類和A類所有的屬性和方法;
- 總結就是:子類擁有父類及父類的父類的所有封裝的屬性與方法。
繼承傳遞示例
class Animal:
def eat(self):
print("吃")
def run(self):
print("跑")
class Dog(Animal):
def bark(self):
print("犬吠")
class Corgi(Dog):
def leg(self):
print("腿很短")
keji = Corgi()
# 子類使用自己的方法
keji.leg()
# 子類使用父類的方法
keji.bark()
# 子類使用父類的父類的方法
keji.eat()
繼承傳遞的分支問題
雖然貓和狗都繼承自動物類,但狗的子類柯基並不能調用貓類的方法,因為柯基並沒有繼承自貓類;
方法重寫
應用場景
- 當父類中的方法滿足不了子類的需求時,可以對方法進行重寫;
- 重寫父類的方法有兩種:
1.覆蓋父類的方法;
2.對父類方法進行擴展;
覆蓋父類的方法
如果在開發中,父類的方法實現和子類的方法實現,完全不同,就可以使用覆蓋在方法,在子類中重新編寫父類的方法實現;
覆蓋方式:在子類中定義一個和父類重名的方法並且實現;
重寫之后,在運行時,只會調用子類中重寫的方法,而不會再調用父類中封裝的方法;
class Dog(Animal):
def bark(self):
print("犬吠")
class Corgi(Dog):
def leg(self):
print("腿很短")
def bark(self):
print("柯基吠")
keji = Corgi()
# 子類使用自己的方法
keji.leg()
# 子類使用重寫父類的方法
keji.bark()
子類中擴寫父類的方法
如果在開發中,子類的方法實現,包含了父類的方法實現,即原本父類封裝的方法是子類方法的一部分,這時候就可以使用子類擴寫父類的方法。
擴寫方式:
- 在子類中重寫父類的方法;
- 在需要的位置,調用父類的方法,用super().父類方法名
- 編寫子類方法其他的代碼
關於super
- 在python中,super是一個特殊的類;
- super()就是使用super類創建出來的對象;
- 經常使用的場景就是,重寫父類方法時,調用在父類中封裝的方法;
擴寫示例
class Dog(Animal):
def bark(self):
print("犬吠")
class Corgi(Dog):
def leg(self):
print("腿很短")
def bark(self):
# 1.針對子類特有的需求,編寫代碼
print("柯基吠")
# 2.在需要的位置,調用父類的方法,用super().父類方法名
super().bark()
# 3.編寫子類方法其他的代碼
print("...")
keji = Corgi()
# 子類使用自己的方法
keji.leg()
# 子類使用 擴展父類的方法
keji.bark()
# 腿很短
# 柯基吠
# 犬吠
# ...
使用父類名調用父類方法(了解)
在python2.x中,如果需要調用父類的方法,還可以用這種方式:
父類名.方法(self)
在python3中,仍然支持這種方法,但不推薦使用,因為一旦父類發生變化,調用位置的父類名同樣需要修改
注意:
- 在開發中,父類名和super()兩種方法不要混用;
- 如果使用當前子類名調用方法,會形成遞歸調用,形成死循環;
使用父類名調用父類方法示例
class Dog(Animal):
def bark(self):
print("犬吠")
class Corgi(Dog):
def leg(self):
print("腿很短")
def bark(self):
print("柯基吠")
# 使用父類名調用父類方法,不推薦使用
Dog.bark(self)
# 注意,如果使用子類調用方法,會出現遞歸調用,形成死循環
# Corgi.bark(self)
print("...")
keji = Corgi()
keji.leg()
keji.bark()
# 腿很短
# 柯基吠
# 犬吠
# ...
多繼承
多繼承概念和語法
概念:子類可以具有多個父類,並且具有多個父類的屬性和方法;
語法:
class 子類名(父類1,父類2,...):
pass
作用:多繼承可以讓子類同時具有多個父類的屬性和方法;
多繼承示例
class A:
def test_a(self):
print("test_a")
class B:
def test_b(self):
print("test_b")
class C(A, B):
pass
c = C()
c.test_a() # test_a
c.test_b() # test_b
多繼承注意事項
- 如果兩個父類之間具有同名屬性或方法,應該盡量避免使用多繼承;
- 如果兩個父類之間具有同名屬性或方法,子類在調用方法時,會優先使用先繼承那個父類的方法;
多繼承方法調用順序示例
class A:
def test(self):
print("test_a")
def demo(self):
print("demo_a")
class B:
def test(self):
print("test_b")
def demo(self):
print("demo_b")
class C(A, B):
pass
c = C()
c.test() # test_a
c.demo() # demo_a
我們把C的繼承父類順序換一下
class A:
def test(self):
print("test_a")
def demo(self):
print("demo_a")
class B:
def test(self):
print("test_b")
def demo(self):
print("demo_b")
class C(B, A):
pass
c = C()
c.test() # test_b
c.demo() # demo_b
MRO--方法搜索順序
__mro__的作用:在創建對象的類,以及繼承的父類中,查找要調用的方法的 順序;
- python中針對類提供了一個內置屬性__mro__可以查看方法搜索順序;
- mro主要用於多繼承時,判斷方法,屬性的調用順序;
__mro__方法使用示例
# 假設C類繼承自B和A
print(C.__mro__)
輸出結果:
# (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
- 在搜索方法時,是按照__mro__搜索結果從左到右的順序查找的;
- 如果在當前類中找到方法,就直接執行,不再查找;
- 如果沒有找到,就查找下一個類中是否有對應的方法,如果找到方法,就直接執行,不再查找;
- 如果找到最后一個類,還沒有找到方法,就報錯。
__mro__方法使用完整示例
class A:
def test(self):
print("test_a")
def demo(self):
print("demo_a")
class B:
def test(self):
print("test_b")
def demo(self):
print("demo_b")
class C(B, A):
pass
c = C()
c.test() # test_b
c.demo() # demo_b
# 確定C類的調用方法順序
print(C.__mro__) # (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
新式類和經典(舊式)類
- 在ipython中,添加了object就是新式類,沒添加就是經典類;可以用dir(對象名)查看新式類和經典類的內置方法;
- 在ipython3下,就算你沒添加object,python解釋器也會給你加上,默認就是新式類;
object是python為所有對象提供的基類,提供有一些內置的方法和屬性,可以用dir函數查看,dir(對象名);
新式類:以object為基類的類,推薦使用;
經典類:不以object為基類的類,不推薦使用;
- 在python3中定義類時,如果沒有指定父類,會默認使用object作為該類的基類--python3中定義的類都是新式類;
- 在python2中,定義類時,如果沒有指定父類,則不會以object作為基類;
新式類和經典類在多繼承時,會影響方法的搜索順序;
為了保證編寫的代碼能同時在python2和python3下運行,以后在定義類時,如果沒有父類,建議統一繼承object類;
class 類名(object):
pass
在ipython下查看內置方法
查看新式類內置方法
class A(object):
pass
a = A()
dir(a)
查看舊式類內置方法
class B:
pass
b = B()
dir(b)