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
= 30000
MIN_SALARY # 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:
= WNC_Employee("Hank", 22000) Hank_wnc
- 檢查一下之前的attribute都繼承下來了:
print(Hank_wnc.name)
#> Hank
print(Hank_wnc.salary)
#> 22000
- 檢查一下method也繼承下來了:
10000)
Hank_wnc.give_raise(print(Hank_wnc.salary)
#> 32000
34.2.2 增加instance-level attribute & method
- 那接下來,就來講如何加入我想新寫的attribute和method
- 例如,對WNC_Employee來說,attribute除了name, salary外,還要新增一個職等(level); method除了原本的
give_raise()
外,還要新增一個promote()
來更新職等的變化:
class Employee_parent:
= 30000
MIN_SALARY 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 這一行
__init__(self, name, salary)
Employee_parent.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看看:
= WNC_Employee("Hank", 22000, 3)
Hank_wnc2 print(Hank_wnc2.name)
#> Hank
print(Hank_wnc2.salary)
#> 22000
print(Hank_wnc2.level)
#> 3
- 用新的function看看
1)
Hank_wnc2.promote(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寫在這
__init__(self, *args, **kwargs)
Employee_parent.self.level = level
def promote(self, num_level):
self.level = self.level + num_level
= WNC_Employee2(name = "Hank", salary = 22000, level = 3)
wnc_hank2 print(wnc_hank2.level)
#> 3
1)
wnc_hank2.promote(print(wnc_hank2.level)
#> 4
34.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):
= 0
MIN_SALARY pass
= WNC_Employee_trial("Hank", 22000)
child = Employee_parent("Hank", 22000)
parent
print(child.MIN_SALARY)
#> 0
print(parent.MIN_SALARY)
#> 30000
print(WNC_Employee_trial.MIN_SALARY)
#> 0
print(Employee_parent.MIN_SALARY)
#> 30000
34.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:
= 30000
MIN_SALARY 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):
__init__(self, *args, **kwargs)
Employee_parent.self.level = level
# 這邊開始修改新method
def give_raise(self, amount, bonus=1): #method名稱同,但多個參數
= amount * bonus
new_amount self, new_amount) #使用parent class的method
Employee_parent.give_raise(
= Employee_parent(name = "Hank", salary = 22000)
Hank_in_parent = WNC_Employee3(name = "Hank", salary = 22000, level = 3)
Hank_in_wnc
=10000)
Hank_in_parent.give_raise(amount=10000, bonus=2)
Hank_in_wnc.give_raise(amount
print(Hank_in_parent.salary)
#> 32000
print(Hank_in_wnc.salary)
#> 42000
class 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")
= Dog()
d = Pig()
p
print(d.eat())
#> Dog is eating
print(p.eat())
#> Pig is eating