註:本文同步刊載在Medium,若習慣Medium的話亦可去那邊看呦!
先來解答昨天的問題吧!
我們可以修改上一篇的範例,並且新增一個compare方法,
由於比較的時候,我們利用self就可以取得現在的物件,
那麼從外面傳入的,我們直接將其稱為b,
要比較的就會是self.score的分數總和,和b.score的分數總和的關係。
這邊繼續介紹好用的小函式:
由於前面我們講過字典,要取所有的值的部分(因為沒有加權嘛,就不用管科目了XD),
是用.values(),那要加總在一起,我們可以用for ... in ...的方式一個一個加起來,
或者,我們可以使用sum()函式,它可以把一個可以算值的一組資料給加總起來。
於是一切就簡單啦!剩下的就是依照結果輸出。
class Student():
def __init__(self, name, score):
self.name = name
self.score = score
def readMyName(self):
print('聽清楚了,我的名字是' + self.name + '!!!')
def compare(self, b):
diff = sum(self.score.values()) - sum(b.score.values())
# 有冒號的式子如果底下程式碼只有一行,也可以選擇直接和判斷式寫成同一行
if diff > 0: print(self.name + '贏了!')
elif diff == 0: print('什麼?竟然平手?!')
else: print('可...可惡,難道,這就是' + b.name + '真正的實力嗎?')
ming = Student('阿明', {'數學':55, '英文':70, '物理':55})
mei = Student('小美', {'數學':90, '英文':88, '物理':100})
howhow = Student('HowHow', {'數學':80, '英文':60, '物理':40})
print(ming.name)
print(mei.name)
print(how.name)
print('\n阿明 vs HowHow')
ming.compare(how)
print('\n阿明 vs 小美')
ming.compare(mei)
print('\n小美 vs HowHow')
mei.compare(how)
我們今天繼續來講物件與類別。
上一篇我們介紹了一個基本的類別是怎麼被做出來的,
也提到了初始化的函式,設定它們的屬性等做法。
在實際運用上,我們會遇到一個問題:
今天遇到更細項的屬性時,我們是否要將其設定到現有的類別呢?
如果這麼做的話,有時候會導致通用性不夠的問題。
例如:
對於一輛車子來說,它是Tesla的話,有可能會有自動駕駛功能,
但如果它是一般的車子的話,自動駕駛顯然不是一個正常會有的功能。
所以當我們有一個名為Car的類別時,
我們想要定義一個Tesla的Car,應該為其增設一個Tesla的類別,
而非直接在Car新增自動駕駛的功能。
但Tesla應該還是擁有Car的基本特性,所以我們可以利用這點,
避免掉重新設定一個全新的類別。這就是我們要介紹的方法:繼承。
在繼承的概念裡,原先Car的類別被稱為「父類別」、「超類別」或「基礎類別」,
(parent/super/base class)
而要做出來的Tesla被稱為「子類別」或「衍生類別」(subclass/derived class)
定義子類別的話,只要在子類別的括號內加入父類別的名稱即可:
class Car():
def whoami(self):
print('I\'m a Car!')
class Tesla(Car):
pass
car = Car()
tla = Tesla()
car.whoami()
tla.whoami()
讀者會發現,在Tesla中儘管尚未定義whoami(),
結果仍然會輸出,而輸出的結果其實是按照Car的whoami()來執行的:
C:\Users\Desolve>python fromzero.py
I'm a Car!
I'm a Car!
因為我們已經從Car這邊繼承過來了其屬性和方法,
所以Tesla也會有whoami的方法。
我們當然也可以把whoami做一下改變,並新增一些我們需要的東西:
class Car():
def whoami(self):
print('I\'m a Car!')
class Tesla(Car):
def __init__(self):
self.pilotmode = 1 # ON: 1, OFF: 0
def whoami(self):
print('I\'m a Tesla, not a trash car!')
def autopilot_switch(self):
self.pilotmode ^= 1 # ^是取XOR(互斥或),所以會在0和1之間切換
if self.pilotmode == 0: print('Auto-pilot mode switch off!')
else: print('Auto-pilot mode switch on!')
car = Car()
tla = Tesla()
car.whoami()
tla.whoami()
tla.autopilot_switch()
tla.autopilot_switch()
上面的示範中,我們為Tesla新增一個屬性,用來表示自動駕駛模式是否開啟,
並提供一個切換開關的方法。但這是Tesla的東西,所以Car並不會有autopilot_switch(),
讀者可以嘗試使用car.autopilot_switch()看看,應該會出現錯誤。
同時留意我們也重寫了一次whoami,
這個將父類別有的函式,重新給予定義的做法,
稱為override(覆寫/覆載)。
整個執行的結果會如下所示:
C:\Users\Desolve>python fromzero.py
I'm a Car!
I'm a Tesla, not a trash car!
Auto-pilot mode switch off!
Auto-pilot mode switch on!
讀者可能會問:
「那如果我們想要覆寫父類別的方法,
又想用到原先的內容,那該怎麼做呢?」
這時候,我們就需要使用super()來取得父類別的東西了!
舉例來說,假設我們對每一輛車都會給它自己一個名字,
在whoami時,會先喊出自己的名字,我們可以修改上面的內容變成這樣:
class Car():
def __init__(self, name):
self.name = name
def whoami(self):
print('My name is ' + self.name)
print('I\'m a Car!')
class Tesla(Car):
def __init__(self, name, mode):
super().__init__(name) # 使用super來對name初始化,看起來稍微多此一舉,但可以保證對於name處理的一致性,之後如果要額外針對Car這個父類別修改時,就可以一起同時影響到Tesla這邊
self.pilotmode = mode
def whoami(self):
super().whoami() # 先喊名字跟喊自己是輛車
print('Also, I\'m a Tesla, not a trash car!') # 這兩行再做只有Tesla會做的事情
print('Auto-pilot mode: ' + str(self.pilotmode))
def autopilot_switch(self):
self.pilotmode ^= 1
if self.pilotmode == 0: print('Auto-pilot mode switch off!\n')
else: print('Auto-pilot mode switch on!\n')
car = Car('CC')
tla = Tesla('TT', 0)
car.whoami()
print()
tla.whoami()
tla.autopilot_switch()
tla.whoami()
tla.autopilot_switch()
其執行結果應該像這樣:
C:\Users\Desolve>python fromzero.py
My name is CC
I'm a Car!
My name is TT
I'm a Car!
Also, I'm a Tesla, not a trash car!
Auto-pilot mode: 0
Auto-pilot mode switch on!
My name is TT
I'm a Car!
Also, I'm a Tesla, not a trash car!
Auto-pilot mode: 1
Auto-pilot mode switch off!
辛苦啦!在接下來的篇幅中,我們會再談談類別方法及靜態方法,
還有討論一下什麼時候該用類別/物件,什麼時候又該用模組的問題。
那我們就明天見囉!
你好:
請問為什麼第一個物件範例Car沒有def init(self)這個初始化的函式?但是後面的範例都有呢?甚麼時候要有def init(self),甚麼時候不用呢?
第一個範例:
class Car():
def whoami(self):
print('I\'m a Car!')
需要__init__函式代表你有需求在做物件初始化的給定一些值或做一些事情,如果不寫的話其實預設就是什麼事情都不做,所以就看你設計的物件是否有需要來決定要不要寫這個函式囉!
謝謝你的回覆>< 你的文章幫了我很多的忙!!