引言:
什麼是抽象類?什麼是歸一化?幹講的話比較枯燥,而且不易了解,今天呢給大家準備一個實際的例子帶大家進行了解學習,然後進行總結。
示例:
- 場景1: 新的任務下來了,需要建立兩個類,一個人類,一個狗類,他們都會移動和進食,可以執行個體化出對象供其他人進行調用。甲程式員開發人類部分的代碼,乙程式員開發狗類部分的代碼。
- 甲的代碼(人類):
# 定義人類
class Human:
# 移動接口
def walk(self):
print("Human is walking")
# 進食接口
def eat(self):
print("Human is eating")
- 乙的代碼(狗類):
# 定義狗類
class Dog:
# 移動接口
def run(self):
print("Dog is running")
# 進食接口
def eat(self):
print("Dog is eating")
- 調用者進行調用:
if __name__ == '__main__':
# 執行個體化得到一個人和一條狗
h1 = Human()
d1 = Dog()
# 調用移動接口
h1.walk()
d1.run()
通過結果我們可以看到,同樣是調用移動接口,但是要使用兩種不同的調用方法,但是對于我們調用者而言,我們并不關心具體實作的細節,我們想要的是同類接口可以去統一的用一種方式去調用
- 場景2: 需求同場景1,但是我們根據調用者的痛點進行了優化:給甲乙程式員開會讓人類和狗類都繼承一個父類(動物類),動物類提供兩個方法移動和進食,人類和狗類重寫父類的這兩個方法,好友善讓調用者統一調用
- 示例代碼:
# 定義動物類
class Animal:
# 移動接口
def move(self):
pass
# 進食接口
def eat(self):
pass
# 定義人類
class Human(Animal):
# 移動接口
def move(self):
print("Human is walking")
# 進食接口
def eat(self):
print("Human is eating")
# 定義狗類
class Dog(Animal):
# 移動接口
def move(self):
print("Dog is running")
# 進食接口
def eat(self):
print("Dog is eating")
if __name__ == '__main__':
# 執行個體化得到一個人和一條狗
h1 = Human()
d1 = Dog()
# 調用移動接口
h1.move()
d1.move()
到這裡看似我們完美解決了這個痛點,但是真的解決了嗎?咱們繼續看故事發展(場景3)
- 場景3: 故事延續場景2,這時候公司新招聘來了一個程式員丙,他的任務是定義一個貓類,由于他并不知道以前調用者的痛點,是以開發時沒有重寫父類提供的方法
- 丙的代碼(貓類):
# 定義貓類
class Cat(Animal):
# 移動接口
def run(self):
print("Dog is running")
# 進食接口
def eat(self):
print("Dog is eating")
- 調用者調用(貓類):
if __name__ == '__main__':
# 執行個體化得到一隻貓
c1 = Cat()
# 調用移動接口
c1.move()
通過執行結果可以發現,調用者在不知情的情況下按照以往的習慣去調用移動接口,并沒有得到想要的接口,因為丙程式員并沒有按照規定把移動接口定義成move,而定義成了其他名字,是以,痛點又出現了,雖然定了調用規則,但是這個規則并沒有限制性,我不按照規則寫,依舊可以進行開發,是以,如何讓我們的規則具有限制性呢,這裡我們可以用到抽象類來進行限制
抽象類
- 抽象類: 隻能被繼承,不能被執行個體化的類, 抽象類中有抽象方法,且子類必須實作抽象方法。
- 歸一化:在程式設計上,這種使得外部調用者無需關心具體細節,可一視同仁的處理實作特定接口的所有對象的思維,叫做歸一化。
- 如果說類是從一堆對象中抽取相同的内容而來的,那麼抽象類就是從一堆類中抽取相同的内容而來的。
- 比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的内容就是水果這個抽象的類,你吃水果時,要麼是吃一個具體的香蕉,要麼是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。
- 從設計角度去看,如果類是從現實對象抽象而來的,那麼抽象類就是基于類抽象而來的。
抽象類的使用步驟:
- 導入abc包:import abc
- 抽象類需添加指定的參數:class 類名(metaclass = abc.ABCMeta)
- 抽象方法需添加裝飾器:@abc.abstractmethod
- 示例代碼:
import abc
# 建立抽象類
class Animal(metaclass=abc.ABCMeta):
# 移動接口
@abc.abstractmethod
def move(self):
pass
# 進食接口
@abc.abstractmethod
def eat(self):
pass
# 定義人類
class Human(Animal):
# 移動接口
def move(self):
print("Human is walking")
# 進食接口
def eat(self):
print("Human is eating")
# 定義狗類
class Dog(Animal):
# 移動接口
def move(self):
print("Dog is running")
# 進食接口
def eat(self):
print("Dog is eating")
# 定義貓類
class Cat(Animal):
# 移動接口
def run(self):
print("Dog is running")
# 進食接口
def eat(self):
print("Dog is eating")
調試:
if __name__ == '__main__':
# 執行個體化得到一個人和一條狗
h1 = Human()
d1 = Dog()
c1 = Cat() # TypeError: Can't instantiate abstract class Cat with abstract method move
# 調用移動接口
h1.move()
d1.move()
c1.move()
通過執行結果我們可以看出,如果我們的類繼承了這個抽象類,如果沒有重寫抽象類内的抽象方法的話,代碼會報錯:TypeError: Can't instantiate abstract class Cat with abstract method move,這樣我們就可以解決上述示例代碼裡我們無法限制子類的痛點了
抽象類不可以被執行個體化
- 執行個體代碼:
import abc
# 建立抽象類
class Animal(metaclass=abc.ABCMeta):
# 移動接口
@abc.abstractmethod
def move(self):
pass
# 進食接口
@abc.abstractmethod
def eat(self):
pass
if __name__ == '__main__':
h1 = Animal() # TypeError: Can't instantiate abstract class Animal with abstract methods eat, move
通過結果我們可以看出,抽象類不可以被執行個體化,否則報錯:TypeError: Can't instantiate abstract class Animal with abstract methods eat, move
總結:
- 抽象類使用步驟:
- 導入abc包:import abc
- 抽象類需添加指定的參數:class 類名(metaclass = abc.ABCMeta)
- 抽象方法需添加裝飾器:@abc.abstractmethod
- 抽象類的優點:
- 調用者可以無需關心具體實作細節,可以一視同仁處理實作特定接口的所有對象。
如果感覺有所收獲的話,可以關注公衆号:橙好測試開發,點個關注不迷路,下期更精彩