---
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.
:::