封裝,繼承,多型,繼承的屬性查詢順序,super()和mro()列表,多型與多型性
阿新 • • 發佈:2021-12-07
一、面向物件的三大特徵
-
封裝:封裝指的就是把資料與功能都整合到一起,聽起來是不是很熟悉,沒錯,我們之前所說的”整合“二字其實就是封裝的通俗說法。
-
繼承
1. 什麼是繼承? # 繼承就是新建類的一種方式,新建的類我們稱為子類或者叫派生類,被繼承的類我們稱為父類或者基類 # 子類可以使用父類中的屬性或者方法 2. 為什麼要用繼承? 類解決了物件與物件之間的程式碼冗餘問題 繼承解決的是類與類之間的程式碼冗餘問題 3. 如何使用繼承? 新式類:繼承了object類的子子孫孫類都是新式類 經典類:沒有繼承了object類的子子孫孫類都是經典類 # 新式類和經典類只有在python2中區分
繼承:類與類之間的繼承指的是什麼’是’什麼的關係(比如人類,豬類,猴類都是動物類)。子類可以繼承/遺傳父類所有的屬性,因而繼承可以用來解決類與類之間的程式碼重用性問題。比如我們按照定義Student類的方式再定義一個Teacher類
class Student: # 定義學生類 school = "南京校區" # 冗餘共同屬性 def __init__(self,name,age,gender): # 學生類與老師類有冗餘部分 self.name = name self.age = age self.gender = gender def choose(self): print("%s 選課成功" %self.name) stu1 = Student("jack",18,"male") stu2 = Student("tom",19,"male") stu3 = Student('lili',29,"female") class Teacher: # 定義老師類 school = "南京校區" def __init__(self,name,age,gender,level): # 老師類與學生類功能多一個level self.name = name self.age = age self.gender = gender self.level = level def score(self): print("%s 正在為學生打分" %self.name) tea1 = Teacher('hxx',18,"male",10) tea2 = Teacher('lxx',38,"male",3)
從上面看出:類Teacher與Student之間存在重複的程式碼,老師與學生都是人類,所以我們可以得出如下繼承關係,實現程式碼重用
-
單繼承下屬性查詢
class Foo: def __f1(self): # _Foo__f1() print('Foo.f1') # 2列印 def f2(self): # print('Foo.f2') # 1 列印 self.__f1() # _Foo__f1() class Bar(Foo): def __f1(self): # # _Bar__f1() print('Bar.f1') ''' 會先從 自己本身Bar找f2 如果沒有 ,就會去父類找,找到了f2 ,f2裡面又執行了雙下f1 為隱藏屬性,隱藏屬性的特徵 在定義的時候 就直接定義好了 ''' obj = Bar() # {} obj.f2()
-
多繼承下的屬性查詢
# 新式類:按照廣度優先查詢
# 經典類:按照深度優先查詢
class A(object):
def test(self):
print('from A')
class B(A):
# def test(self):
# print('from B')
pass
class C(A):
# def test(self):
# print('from C')
pass
class D(B):
# def test(self):
# print('from D')
pass
class E(C):
# def test(self):
# print('from E')
pass
class F(D, E):
# def test(self):
# print('from F')
pass
f1 = F()
f1.test()
1、繼承的實現原理
-
繼承順序
Python中子類可以同時繼承多個父類,如A(B,C,D)
如果繼承關係為非菱形結構,則會按照先找B這一條分支,然後再找C這一條分支,最後找D這一條分支的順序直到找到我們想要的屬性
如果繼承關係為菱形結構,那麼屬性的查詢方式有兩種,分別是:深度優先和廣度優先
二、super()和mro()列表
-
方式二: super()返回一個特殊的物件,該物件會參考發起屬性查詢的那一個類的mro列表,去當前類的父類中找屬性,嚴格依賴繼承
class People:
school = "南京校區"
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
class Teacher(People):
# 空物件,'hxx',18,"male",10
def __init__(self,name,age,gender,level):
# People.__init__(self,name,age,gender)
super(Teacher,self).__init__(name,age,gender)
self.level = level
def score(self):
print("%s 正在為學生打分" %self.name)
tea1 = Teacher('hxx',18,"male",10) # 空物件,'hxx',18,"male",10
print(tea1.__dict__)
# 案例:
class A: # [A,object]
def test(self):
print("from A")
super().test()
class B:
def test(self):
print('from B')
class C(A,B): # [C,A,B,object]
pass
# obj=C()
# obj.test()
obj1 = A()
obj1.test()
三、多型與多型性
-
多型:同一種事物有多種形態
-
動物有多種形態:如狗、貓、豬
-
class Animal: # 同一類事物:動物
def talk(self):
pass
class Dog(Animal): # 動物的形態之一:狗
def talk(self):
print("汪汪汪")
class Cat(Animal): # 動物的形態之二:貓
def talk(self):
print('喵喵!')
class Pig(Animal): # 動物的形態之一:豬
def talk(self):
print("哼哼哼!")
# 例項化得到三個物件
obj1 = Dog()
obj2 = Cat()
obj3 = Pig()
# 父類的功能是用來統一子類的,定標準的,只要看父類功能就知道子類也有
-
多型性指的是可以在不用考慮物件具體型別的情況下而直接使用物件,這就需要在設計時,把物件的使用方法統一成一種:例如obj1、obj2、obj3都是動物,但凡是動物肯定有talk方法,於是我們可以不用考慮它們三者的具體是什麼型別的動物,而直接使用
obj1.talk()
# 汪汪汪
obj2.talk()
# 喵喵!
obj3.talk()
# 哼哼哼!
-
在多型性背景下用的繼承,父類的功能其實不是真的拿來給子類用的,而是用來給子類定標準的,用來統一子類的,只需要把父類的功能規定好了,子類就肯定有,這樣的好處是隻要看父類就知道子類有什麼功能
1.更進一步,我們可以定義一個統一的介面來使用
def talk(animal): # 定義一個函式talk 傳進來引數就叫動物
animal.talk() # 只要是動物就肯定有talk方法
# 三個物件統一用一個函式talk去調,用起來統一了
talk(obj1)
talk(obj2)
talk(obj3)
綜上我們得知,多型性的本質在於不同的類中定義有相同的方法名,這樣我們就可以不考慮類而統一用一種方式去使用物件,可以通過在父類引入抽象類的概念來硬性限制子類必須有某些方法名
import abc
# 指定metaclass屬性將類設定為抽象類,抽象類本身只是用來約束子類的,不能被例項化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 該裝飾器限制子類必須定義有一個名為talk的方法
def talk(self): # 抽象方法中無需實現具體的功能
pass
class Dog(Animal): # 但凡繼承Animal的子類都必須遵循Animal規定的標準
def talk(self):
print("汪汪汪")
class Cat(Animal):
def talk(self):
print('喵喵!')
class Pig(Animal):
def tell(self): # 若子類中沒有一個名為talk的方法則會丟擲異常TypeError,無法例項化
print("哼哼哼!")
obj1 = Dog()
obj2 = Cat()
obj3 = Pig()
obj1.talk()
obj2.talk()
obj3.talk()
-
但其實我們完全可以不依賴於繼承,只需要製造出外觀和行為相同物件,同樣可以實現不考慮物件型別而使用物件,這正是Python崇尚的“鴨子型別”(duck typing):“如果看起來像、叫聲像而且走起路來像鴨子,那麼它就是鴨子”。比起繼承的方式,鴨子型別在某種程度上實現了程式的鬆耦合度,如下
# 三者看起來都像,三者都有talk功能,然而它們並沒有直接的關係,且互相獨立
class Dog:
def talk(self):
print("汪汪汪")
class Cat:
def talk(self):
print('喵喵!')
class Pig:
def talk(self):
print("哼哼哼!")