34 Encapsulation, Inheritacne, and Polymorphism
- 這一章要介紹
34.2 繼承(inheritance)
- 繼承的意思是,我們在define一個class的時候,可以繼承另一個class的所有attribue和methods
-
parent class指的是給別人繼承的class,又稱為base class
- child class指的是繼承別人的class,又稱為deived class
34.2.1 simple inheritance
- 例如,我先寫一個class叫Employee_parent,如下:
class Employee_parent:
# class-level attribute
MIN_SALARY = 30000
# instance-level attribute
def __init__(self, name, salary):
self.name = name
self.salary = salary
# instance-level method
def give_raise(self, amount):
self.salary += amount- 這個class有兩個attribute,和一個method
- 那如果今天我想寫一個
WNC_Employee,他想繼承Employee_parent的這些attribute & methods,那就這樣寫:
class WNC_Employee(Employee_parent): #把要繼承的Class name塞入
pass- 現在我們可以照往常輸入name和salary來initialize一個
WNC_Employee的instance:
Hank_wnc = WNC_Employee("Hank", 22000)- 檢查一下之前的attribute都繼承下來了:
print(Hank_wnc.name)
#> Hank
print(Hank_wnc.salary)
#> 22000- 檢查一下method也繼承下來了:
Hank_wnc.give_raise(10000)
print(Hank_wnc.salary)
#> 3200034.2.2 增加instance-level attribute & method
- 那接下來,就來講如何加入我想新寫的attribute和method
- 例如,對WNC_Employee來說,attribute除了name, salary外,還要新增一個職等(level); method除了原本的
give_raise()外,還要新增一個promote()來更新職等的變化:
class Employee_parent:
MIN_SALARY = 30000
def __init__(self, name, salary):
self.name = name
self.salary = salary
def give_raise(self, amount):
self.salary += amount
class WNC_Employee(Employee_parent): #把要繼承的Class name塞入
# 到目前為止,原本Employee有的東西,WNC_Employee都有了
def __init__(self, name, salary, level = 3): # 要多加的level寫在這
# call之前Emplolyee的init來用,所以寫了這步,
# 等於是少寫self.name, self.salary = name, salary 這一行
Employee_parent.__init__(self, name, salary)
self.level = level
def promote(self, num_level):
self.level = self.level + num_level- 看一下
__init__那邊,一開始的init是為了initialize這個新的class的instance用的,而因為他繼承了Employee,所以原本描述Employee的所有attribute都要放入,所以一樣填入”name”和”salary”,而這個新的Class還要有新的attribute,叫”level”,用來描述職等。而這邊的self指的是WNC_Employee的instance的代名詞。
- 那第二行出現的
Employee_parent.__init__(self, name, salary),指的是我要套用之前Employee這個class的__init__函數,第一個self指的還是WNC_Employee的instance,但因為前面講過,他同時也是Employee的instance,所以可以這樣套進來。然後後面的name和salary就是用第一點傳入的name和salary。那這樣套用function後,他就會執行self.name = name,self.salary = salary這兩行,所以,我只剩下self.level = level這行自己寫下來就好 - 這邊提醒一下,沒有規定一定要用
Employee_parent.__init__(),你想要累累的寫self.name = name; self.salary = salary也可以。上面的寫法只是讓你省力,所以大家都這樣寫而已。 - 我們現在initialize一個instance看看:
Hank_wnc2 = WNC_Employee("Hank", 22000, 3)
print(Hank_wnc2.name)
#> Hank
print(Hank_wnc2.salary)
#> 22000
print(Hank_wnc2.level)
#> 3- 用新的function看看
Hank_wnc2.promote(1)
print(Hank_wnc2.level)
#> 4- 看起來很不錯,但如果我原本parent class的attribute就一大堆的話,我不就又要copy一堆attribute到我的
__init__()裡面?其實不用,我們可以用*args和**kwargs直接代稱掉原本的arguments,例如以下這種寫法
class WNC_Employee2(Employee_parent): #把要繼承的Class name塞入
# 到目前為止,原本Employee有的東西,WNC_Employee都有了
def __init__(self, level, *args, **kwargs): # 要多加的level寫在這
Employee_parent.__init__(self, *args, **kwargs)
self.level = level
def promote(self, num_level):
self.level = self.level + num_level
wnc_hank2 = WNC_Employee2(name = "Hank", salary = 22000, level = 3)
print(wnc_hank2.level)
#> 3
wnc_hank2.promote(1)
print(wnc_hank2.level)
#> 434.2.3 增加class-level attribute
- 這其實很好理解,你都繼承原本的class了,所以包括原本的class level attribute, instance attribute, methods全都繼承下來
- 所以,可以看一下剛剛的
WNC_Employee的class level attribute:MIN_SALARY在不在
WNC_Employee.MIN_SALARY
#> 30000- 在嘛,合理。那這一節的重點,是放在我如果修改child class的class-level attribute,parent class的class-level attribute會不會跟著變?
- 直接講結論:class level attribute可以繼承,而child class的class level attribute可以overwritten掉原本parent class的class level attribute,但不會改變parent class的class level attribute
class WNC_Employee_trial(Employee_parent):
MIN_SALARY = 0
pass
child = WNC_Employee_trial("Hank", 22000)
parent = Employee_parent("Hank", 22000)
print(child.MIN_SALARY)
#> 0
print(parent.MIN_SALARY)
#> 30000print(WNC_Employee_trial.MIN_SALARY)
#> 0
print(Employee_parent.MIN_SALARY)
#> 3000034.3 多形
- 多形的意思是,子類別雖然繼承父類別的methods,但我可以命名一個名稱一模一樣的method,讓他over-ride原先的method,這樣就達到名稱一樣,但功能不一樣的現象,我們叫”多形”
- 以
WNC_Employee這個class為例,他繼承Employee_parent,所以也繼承了give_raise()這個method
- 那如果WNC這間公司是個佛心公司,他的加薪方式,跟一般公司不同(你輸入要加的薪水後,我還會給你額外的加成福利),那我們有兩種做法:
- 寫一個新的method叫
give_raise_new()
- 維持舊名稱
give_raise(),但內容被我置換成wnc版本 -> 建議這麼做,而這麼做的結果就是”多形”
- 寫一個新的method叫
- 直接看例子:
class Employee_parent:
MIN_SALARY = 30000
def __init__(self, name, salary):
self.name = name
self.salary = salary
def give_raise(self, amount):
self.salary += amount
class WNC_Employee3(Employee_parent):
def __init__(self, level, *args, **kwargs):
Employee_parent.__init__(self, *args, **kwargs)
self.level = level
# 這邊開始修改新method
def give_raise(self, amount, bonus=1): #method名稱同,但多個參數
new_amount = amount * bonus
Employee_parent.give_raise(self, new_amount) #使用parent class的method
Hank_in_parent = Employee_parent(name = "Hank", salary = 22000)
Hank_in_wnc = WNC_Employee3(name = "Hank", salary = 22000, level = 3)
Hank_in_parent.give_raise(amount=10000)
Hank_in_wnc.give_raise(amount=10000, bonus=2)
print(Hank_in_parent.salary)
#> 32000
print(Hank_in_wnc.salary)
#> 42000class Animal:
def eat(self):
return("Animal is eating")
class Dog:
def eat(self):
return("Dog is eating")
class Pig:
def eat(self):
return("Pig is eating")
d = Dog()
p = Pig()
print(d.eat())
#> Dog is eating
print(p.eat())
#> Pig is eating