iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
Software Development

從零開始學Python系列 第 13

[Day 13] 從零開始學Python - 物件與類別:我們不一樣,每個人都有不同的際遇(中)

註:本文同步刊載在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!

辛苦啦!在接下來的篇幅中,我們會再談談類別方法及靜態方法,
還有討論一下什麼時候該用類別/物件,什麼時候又該用模組的問題。

那我們就明天見囉!


上一篇
[Day 12] 從零開始學Python - 物件與類別:我們不一樣,每個人都有不同的際遇(上)
下一篇
[Day 14] 從零開始學Python - 物件與類別:我們不一樣,每個人都有不同的際遇(下)
系列文
從零開始學Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
l142linda
iT邦新手 5 級 ‧ 2022-01-03 14:04:09

你好:
請問為什麼第一個物件範例Car沒有def init(self)這個初始化的函式?但是後面的範例都有呢?甚麼時候要有def init(self),甚麼時候不用呢?

第一個範例:
class Car():
    def whoami(self):
        print('I\'m a Car!')
Desolve iT邦新手 5 級 ‧ 2022-01-03 15:57:18 檢舉

需要__init__函式代表你有需求在做物件初始化的給定一些值或做一些事情,如果不寫的話其實預設就是什麼事情都不做,所以就看你設計的物件是否有需要來決定要不要寫這個函式囉!

l142linda iT邦新手 5 級 ‧ 2022-01-03 16:46:03 檢舉

謝謝你的回覆>< 你的文章幫了我很多的忙!!

我要留言

立即登入留言