天天看點

python測試開發日記:抽象類和歸一化,有點意思

作者:橙好測試開發

引言:

什麼是抽象類?什麼是歸一化?幹講的話比較枯燥,而且不易了解,今天呢給大家準備一個實際的例子帶大家進行了解學習,然後進行總結。

示例:

  • 場景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,而定義成了其他名字,是以,痛點又出現了,雖然定了調用規則,但是這個規則并沒有限制性,我不按照規則寫,依舊可以進行開發,是以,如何讓我們的規則具有限制性呢,這裡我們可以用到抽象類來進行限制

抽象類

  • 抽象類: 隻能被繼承,不能被執行個體化的類, 抽象類中有抽象方法,且子類必須實作抽象方法。
  • 歸一化:在程式設計上,這種使得外部調用者無需關心具體細節,可一視同仁的處理實作特定接口的所有對象的思維,叫做歸一化。

- 如果說類是從一堆對象中抽取相同的内容而來的,那麼抽象類就是從一堆類中抽取相同的内容而來的。

- 比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的内容就是水果這個抽象的類,你吃水果時,要麼是吃一個具體的香蕉,要麼是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。

- 從設計角度去看,如果類是從現實對象抽象而來的,那麼抽象類就是基于類抽象而來的。

抽象類的使用步驟:

  1. 導入abc包:import abc
  2. 抽象類需添加指定的參數:class 類名(metaclass = abc.ABCMeta)
  3. 抽象方法需添加裝飾器:@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
  • 抽象類的優點:
    • 調用者可以無需關心具體實作細節,可以一視同仁處理實作特定接口的所有對象。

如果感覺有所收獲的話,可以關注公衆号:橙好測試開發,點個關注不迷路,下期更精彩