--- title: Python Class(下) image: https://i.imgur.com/hfBqYPs.jpg tags: cpsd --- ## Class 類別(進階) > A language feature would not be worthy of the name “class” without supporting inheritance. > 如果沒有支援繼承,「class」這個語言特色就不值得被稱為 class。 > --Python 官方說明書 ### Inheritance 繼承 ```python class Car: def __init__(self, engine, price, speed): self.engine = engine self.price = price self.speed = speed def drive(self): print('GoGO') def accelerate(self): print('go ahead') class AirPlane: def __init__(self, engine, price, height): self.engine = engine self.price = price self.height = height def drive(self): print('GoGO') def fly(self): print('up') class Motorcycle: def __init__(self, engine, price): self.engine = engine self.price = price def drive(self): print('GoGO') def easter_egg(self): print('Never gonna give you up') ``` 以上三個 Class 有大量重複的屬性跟方法,當程式碼達上百行或更多這樣的行為可能更多,違反**物件導向程式設計**中的 [一次且僅一次](https://zh.wikipedia.org/wiki/%E4%B8%80%E6%AC%A1%E4%B8%94%E4%BB%85%E4%B8%80%E6%AC%A1) > Once and only once, OAOO > 又稱為 Don't repeat yourself, DRY > 或 One rule, one place > 但有時,為了可讀性,或避免[耦合](https://zh.wikipedia.org/wiki/%E8%80%A6%E5%90%88%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8) "耦合性 (計算機科學)"),或過早[重構](https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84 "代碼重構"),應放棄DRY原則 ```python class Transportation: def __init__(self, engine, price): self.engine = engine self.price = price def drive(self): print('GoGO') class Car(Transportation): def __init__(self, engine, price, speed): super().__init__(engine, price) self.speed = speed def accelerate(self): print('go ahead') class AirPlane(Transportation): def __init__(self, engine, price, height): super().__init__(engine, price) self.height = height def fly(self): print('up') class Motorcycle(Transportation): def __init__(self, engine, price): super().__init__(engine, price) def easter_egg(self): print('Never gonna give you up') ``` 這裡的 `Transportation` 稱為 Base Class (父類別) 而 `Car`, `Airplane`, `Motorcycle` 則是 `Transportation` 的 Sub Class (子類別) > Python3 可以直接用 super() 來呼叫父類別 > Python2 則必須用 super(Class, self) > 如範例程式就要改成 `super(Transportation, self).__init__` > :::info Python 提供 `isinstance(object_name, class_name)` 來檢查一個物件與一個類別的關係 ```python class Student: pass class Teacher: pass sun = Teacher() print(isinstance(sun, Student)) # False print(isinstance(sun, Teacher)) # True ``` `isinstance` 也能檢查到父類別 還有 `issubclass(object_name, object_name)` 提供了檢查類別間關係的方式 ```python class Student: pass class Teacher(Student): pass sun = Teacher() print(isinstance(sun, Student)) # True print(isinstance(sun, Teacher)) # True print(issubclass(Student, Teacher)) # False print(issubclass(Teacher, Student)) # True print(issubclass(Student, object)) # True 所有 class 都間接或直接繼承了 object print(issubclass(Teacher, object)) # True ``` ::: #### Method Overriding 方法覆寫 ```python class Computer: def hi(self): print("I'm computer.") class AMD(Computer): def hi(self): print("I'm AMD.") A = AMD() A.hi() # output: I'm AMD. ``` 如果子類別有與父類別同名的方法,則子類別會覆寫該方法,稱為 Method Overriding 需要使用同名方法的話可以在子類別中以 `super` 呼叫 ```python class AMD(Computer): def hi(self): super().hi() print("I'm AMD.") A = AMD() A.hi() # output: # I'm computer. # I'm AMD. ``` #### Multi-Level Inheritance 多層繼承 ```python class Animal: def __init__(self, height, weight): self.height = height self.weight = weight def hello(self): print(f"I'm {self.height} cm tall and weight {self.weight} kg.") def fly(self): pass class Human(Animal): def __init__(self, height, weight, age): super().__init__(height, weight) self.age = age def hello(self): super().hello() print(f"I'm {self.age} years old.") class Taiwanese(Human): def __init__(self, height, weight, age): super().__init__(height, weight, age) class Student(Taiwanese): def __init__(self, height, weight, age, school): super().__init__(height, weight, age) self.school = school def hello(self): super().hello() print(f"I study at {self.school}.") magical = Student(165, 50, 19, 'NTNU') magical.hello() # output: # I'm 165 cm tall and weight 50 kg. # I'm 19 years old. # I study at NTNU. ``` 多層繼承通常不建議超過兩層,否則程式碼較多時不易維護,且因較為複雜有時會繼承到不該繼承的東西(像是 `class Human` 繼承 `class Animal` 就莫名其妙會飛了) #### Multiple Inheritance 多重繼承 ```python class Phone: def __init__(self, cpu, size): self.cpu = cpu self.size = size def hi(self): print("I'm Phone.") class Apple: def __init__(self, model): self.model = model def hi(self): print("I'm Apple.") class iPhone(Phone, Apple): def __init__(self, model, cpu, size, gen): Phone.__init__(self, cpu, size) Apple.__init__(self, model) self.gen = gen def hello(self): print(f"I'm gen {self.gen} {self.model}." ) myphone = iPhone('iPhone mini', 'A15', 5.4, 13) myphone.hi() myphone.hello() # output: # I'm Phone. # I'm gen 13 iPhone mini. ``` 多重繼承是一種類別同時有兩個或以上父類別 要特別注意內部如果有同名屬性或方法會依繼承順序以遞迴方式繼承 (範例中是先 `Phone` 再 `Apple` ,因此調用 `hi()` 時的輸出為 `Phone` 的 `I'm Phone`) 包含魔術方法也是,因此使用多重繼承時的初始化方式也不一樣 除了魔術方法在命名上必須按照標準,在使用多重繼承時應盡可能避免同名方法,免得換別人維護那份程式碼的時候因為交換繼承的順序而產生不如預期的結果 :::success 在某些情形下依然能使用 `super()` 來調用多個父類別的方法,有空的可以試試看 Just trial and error. :::