Pin-Jiun / Python

Python Document
0 stars 0 forks source link

17-Inheritance #17

Open Pin-Jiun opened 2 years ago

Pin-Jiun commented 2 years ago

繼承(Inheritance)


一、如何使用Python繼承(Inheritance)

在實務上開發應用程式時,隨著類別(Class)的增加,可能會發現有些類別(Class)擁有共同的屬性(Attribute)或方法(Method),如下

# 汽車類別
class Car:
    # 駕駛方法
    def drive(self):
        print("drive method is called.")
    # 加速方法
    def accelerate(self):
        print("accelerate method is called.")
# 飛機類別
class Airplane:
    # 駕駛方法
    def drive(self):
        print("drive method is called.")
    # 飛行方法
    def fly(self):
        print("fly method is called.")

範例中的Car及Airplane類別(Class)皆擁有drive()方法,當其有數百行程式碼時,在應用程式中重複出現是不好的且違背DRY(Don't Repeat Yourself)原則,未來這個方法(Method)有問題或邏輯改變了,就要修改好幾個地方,難於維護。

這時候就可以使用物件導向的繼承(Inheritance)設計,將共同的屬性(Attribute)或方法(Method)定義在一個類別(Class)中,而其它類別(Class)則透過繼承(Inheritance)的方式來擁有它,如下範例:

# 交通工具(基底類別)
class Transportation:
    # 建構式
    def __init__(self):
        self.color = "white"  #顏色屬性
    # 駕駛方法
    def drive(self):
        print("drive method is called.")
# 汽車子類別
class Car(Transportation):
    # 加速方法
    def accelerate(self):
        print("accelerate is method called.")
# 飛機子類別
class Airplane(Transportation):
    # 飛行方法
    def fly(self):
        print("fly method is called.")

我們將Car及Airplane類別(Class)的共同方法drive()定義在一個新的Transportation類別(Class)中,並且新增一個屬性(Attribute)為color。

Transportation類別(Class)就叫父類別或基底類別(Base Class),而Car及Airplane類別(Class)就稱為子類別(Sub Class),在類別名稱的地方透過括號的方式來繼承(Inheritance),藉此擁有父類別公開的屬性(Attribute)及方法(Method),如下範例:

mazda = Car()
mazda.drive()
print(mazda.color)
drive method is called.
white

可以看到Car(子類別)的物件mazda擁有Transportation(父類別)的color屬性(Attribute)及drive()方法(Method)

在淺談Python類別(Class)中有提到Python提供了isinstance()方法來判斷類別(Class)與物件(Object)之間的關係,而這邊Python也提供了issubclass()方法來判斷類別(Class)之間的關係,如下範例:

# Airplane是否為Transportation的子類別
print(issubclass(Airplane, Transportation))  # True
# Airplane是否為object的子類別
print(issubclass(Airplane, object))  # True
# Airplane是否為Car的子類別
print(issubclass(Airplane, Car))  # Fasle

為什麼我們會說Python的所有類別(Class)皆為物件(Object)?也就是因為Python的所有類別(Class)直接或間接的繼承(Inheritance)了物件類別(object),所以我們可以看到第二個print()的結果為True。


二、方法覆寫(Method Overriding)

當子類別中定義了和父類別同名的方法(Method),這時候子類別的物件(Object)呼叫這個同名方法時,其中的實作內容將會覆蓋掉父類別的同名方法,這就叫做方法覆寫(Method Overriding),如下範例

# 交通工具(基底類別)
class Transportation:
    # 駕駛方法
    def drive(self):
        print("Base class drive method is called.")
# 汽車子類別
class Car(Transportation):
    # 駕駛方法
    def drive(self):
        print("Sub class drive method is called.")
mazda = Car()
mazda.drive() #Sub class drive method is called.

這時候如果我們想在子類別中執行父類別的方法(Method)時,則可以使用super()內建方法來達成

# 交通工具(基底類別)
class Transportation:
    # 駕駛方法
    def drive(self):
        print("Base class drive method is called.")
# 汽車子類別
class Car(Transportation):
    # 駕駛方法
    def drive(self):
        super().drive()
        print("Sub class drive method is called.")
mazda = Car()
mazda.drive()

從執行結果可以看到,子類別透過super()內建方法執行父類別的drive()方法(Method)後,接著執行子類別的後續實作。


三、多層繼承(Multi-Level Inheritance)

繼承(Inheritance)的層級超過一層以上

# 動物類別
class Animal:
    pass
# 鳥類類別
class Bird(Animal):
    # 飛行方法
    def fly(self):
        print("fly")
# 鴨子類別
class Duck(Bird):
    pass
duck = Duck()
duck.fly()

範例中的Bird類別(Class)繼承(Inheritance)了Animal類別(Class),而Duck類別(Class)又再繼承(Inheritance)了Bird類別,形成了多層繼承(Multi-Level Inheritance)的關係。

各位有沒有發現問題?接著我們就可以建立鴨子物件(Object),並且呼叫父類別的fly()方法(Method),但是就邏輯上來說,鴨子不會飛阿~

從這邊就可以知道,雖然繼承(Inheritance)在程式碼的重用(Reusable)上非常的好,但是如果沒有適當的使用就會像此範例一樣產生邏輯上的錯誤。另外,在多層繼承(Multi-Level Inheritance)時,建議別超過兩層,否則反而會增加程式碼的複雜度及難以維護。


四、多重繼承(Multiple Inheritance)

子類別繼承(Inheritance)一個以上的父類別,如果沒有適當的使用同樣會產生問題

# 動物類別
class Animal:
    def eat(self):
        print("Animal eat method is called.")
# 鳥類類別
class Bird:
    def eat(self):
        print("Bird fly method is called.")
# 鴨子類別
class Duck(Animal, Bird):
    pass
duck = Duck()
duck.eat()  #Animal eat method is called.

Use the pass keyword when you do not want to add any other properties or methods to the class.

我們知道鴨子既是動物也是鳥類,所以範例中Duck類別(Class)多重繼承(Multiple Inheritance)Animal及Bird類別(Class),接著我們建立duck物件(Object),並且呼叫eat()方法(Method),為什麼是執行Animal類別的eat()方法(Method)而不是Bird類別的eat()方法(Method)呢?

因為Python編譯器在執行多重繼承(Multiple Inheritance)時,會先檢查Duck類別(Class)是否有eat()方法(Method),以這個範例來說沒有,接著Python編譯器會尋找多重繼承(Multiple Inheritance)的第一個類別Animal是否擁有,有的話即執行,並且停止搜尋第二個類別。

如果未來有新的開發人員加入,把Duck類別(Class)的多重繼承類別(Multiple Inheritance)順序對掉,這樣的情況對程式開發來說非常的危險,因為產生了不如預期的執行結果。

會發生這樣的問題就是因為多重繼承(Multiple Inheritance)的各類別(Class)有相同的方法(Method)。要避免此問題,就是各類別應各司其職,避免有相同的方法,如下範例,否則就會產生以上範例的問題。


# 動物類別
class Animal:
    def eat(self):
        print("Animal eat method is called.")
# 鳥類類別
class Bird:
    def walk(self):
        print("Bird walk method is called.")
# 鴨子類別
class Duck(Animal, Bird):
    pass
duck = Duck()
duck.eat()

從範例中可以看到,Animal和Bird類別(Class)中沒有共同的方法(Method)且有各自的行為,這時候Duck類別(Class)的多重繼承(Multiple Inheritance)就不會產生問題。


Add the init() Function

The init() function is called automatically every time the class is being used to create a new object.

class Person:
  def __init__(self, fname, lname):
    self.firstname = fname
    self.lastname = lname

  def printname(self):
    print(self.firstname, self.lastname)

#Use the Person class to create an object, and then execute the printname method:

x = Person("John", "Doe")
x.printname()

add the init() function to the Student class:

class Student(Person):
  def __init__(self, fname, lname):
    #add properties etc.

When you add the init() function, the child class will no longer inherit the parent's init() function.

To keep the inheritance of the parent's init() function, add a call to the parent's init() function:

class Student(Person):
  def __init__(self, fname, lname):
    Person.__init__(self, fname, lname)

super() Function

Python also has a super() function that will make the child class inherit all the methods and properties from its parent:

class Student(Person):
  def __init__(self, fname, lname):
    super().__init__(fname, lname)

參考資料 https://www.learncodewithmike.com/2020/01/python-inheritance.html https://www.w3schools.com/python/python_inheritance.asp

Pin-Jiun commented 1 year ago

https://blog.csdn.net/a__int__/article/details/104600972 https://iampennywu.gitbooks.io/introducing-python/content/chapter6-5.html