Python 新手之旅 V1.10 - 單元 4 - Python 與物件導向程式設計



物件導向程式設計 (object-oriented programming) 是對真實世界的比擬,例如我們設計一個模擬租借腳踏車的系統,租腳踏車的是人,利用電子錢包扣款租借,因此「人」、「電子錢包」、「腳踏車」在這個系統中都會是獨立的物件 (object) 。


以下寫個簡單程式來模擬,如下


001class Person:
002    name = ""
003    wallet = None
004    bike = None
005 
006    def __init__(self, name):
007        self.name = name
008        m = "A person "
009        m += self.name
010        m += " is here."
011        print(m)
012 
013    def setWallet(self, wallet):
014        self.wallet = wallet
015        m = self.name
016        m += " has the new wallet."
017        print(m)
018 
019    def rentBike(self, bike):
020        self.wallet.balance -= 50
021        self.bike = bike
022        m = self.name
023        m += "'s wallet now has "
024        m += str(self.wallet.balance)
025        m += " dollars."
026        print(m)
027 
028class ElectronWallet:
029    balance = 0
030    person = None
031 
032    def __init__(self, person, balance):
033        self.person = person
034        self.balance = balance
035        m = "A new wallet has "
036        m += str(self.balance)
037        m += " dollars."
038        print(m)
039 
040class RenterBike:
041    person = None
042 
043    def __init__(self, person):
044        self.person = person
045        m = "A bike is rented by "
046        m += self.person.name
047        m += "."
048        print(m)
049 
050someone = Person("John")
051newWallet = ElectronWallet(someone, 1000)
052someone.setWallet(newWallet)
053newBike = RenterBike(someone)
054someone.rentBike(newBike)

「人」用類別 (class) Person 來模擬


001class Person:
002    name = ""
003    wallet = None
004    bike = None
005 
006    def __init__(self, name):
007        self.name = name
008        m = "A person "
009        m += self.name
010        m += " is here."
011        print(m)
012 
013    def setWallet(self, wallet):
014        self.wallet = wallet
015        m = self.name
016        m += " has the new wallet."
017        print(m)
018 
019    def rentBike(self, bike):
020        self.wallet.balance -= 50
021        self.bike = bike
022        m = self.name
023        m += "'s wallet now has "
024        m += str(self.wallet.balance)
025        m += " dollars."
026        print(m)

Person 的屬性 wallet 是對「電子錢包」的連結,因為「人」也可以沒有「電子錢包」,所以初值設定成 None ,等到「人」有了「電子錢包」之後,再用 setWallet() 設定「電子錢包」。


而屬性 bike 則是對「腳踏車」的連結,還沒有租借當然也沒有腳踏車,所以初值也是設定成 None ,實際租借則是使用 rentBike() 方法,此時會從「電子錢包」扣款,然後完成 bike 的設定。


由上看來,可以發現 Person 對「電子錢包」及「腳踏車」兩個物件都有著緊密的關係,至於「電子錢包」用 ElectronWallet 來模擬,如下


028class ElectronWallet:
029    balance = 0
030    person = None
031 
032    def __init__(self, person, balance):
033        self.person = person
034        self.balance = balance
035        m = "A new wallet has "
036        m += str(self.balance)
037        m += " dollars."
038        print(m)

這是採記名制的「電子錢包」,因此每個 ElectronWallet 利用屬性 person 對應到專屬的 Person 物件,當然也不排除一個 Person 可以有多個 ElectronWallet


最後「腳踏車」用 RenterBike 來模擬,如下


040class RenterBike:
041    person = None
042 
043    def __init__(self, person):
044        self.person = person
045        m = "A bike is rented by "
046        m += self.person.name
047        m += "."
048        print(m)

RenterBike 同樣用屬性 person 保持對 Person 的連結,也默許一個 Person 可以租借多台 RenterBike


我們用以下程式碼模擬租借過程


050someone = Person("John")
051newWallet = ElectronWallet(someone, 1000)
052someone.setWallet(newWallet)
053newBike = RenterBike(someone)
054someone.rentBike(newBike)

檔名儲存為 objectdemo.py ,執行結果如下


$ python3 objectdemo.py
A person John is here.
A new wallet has 1000 dollars.
John has the new wallet.
A bike is rented by John.
John's wallet now has 950 dollars.
$

以上說明物件導向程式設計著重在物件與物件的互動跟連結, Python 基本上就是按照物件導向的概念設計的程式語言,涵蓋物件導向的三大特性


  • 封裝 (encapsulation)
  • 繼承 (inheritance)
  • 多型 (polymorphism)

封裝的意思就是設定屬性的存取權限,一般而言, Python 中類別的屬性都是公開的,也就是建立物件後就能直接運用小數點存取屬性。


如果不想要某個類別中的屬性被任意存取,就需要將屬性封裝了。封裝是在屬性前加上兩條底線,例如


001class EC:
002    __x = 0

這樣如果還想直接用小數點存取屬性 __x ,就會出現找不到的訊息


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'EC' object has no attribute '__x'

繼承的意思則是取出物件系統中多個類別共通的屬性及方法,將共通的部分設計成父類別 (superclass) ,再設計不同的子類別 (subclass) 去實作不同的部分,同時讓子類別繼承自父類別,這樣讓每種子類別都能有共通的屬性及方法。


例如有以下的類別 SC


001class SC:
002    a = 11
003 
004    def do_something():
005        print("Hello, SC!")

繼承的寫法就是類別識別字後加上小括弧,然後小括弧中放父類別的名稱,例如以下的 SC2


001class SC2(SC):
002    b = 22
003 
004    def do_something():
005        print("Hello, SC2!")

這樣 SC2 除了有屬性 b 跟方法 do_something() 之外,也會有屬性 a ,至於原本 SCdo_something() 則被 SC2do_something() 改寫 (override) 了。


這個情況是說,子類別如果設置跟父類別相同名稱的屬性或方法,基本上會以子類別的版本為主,如果子類別想要使用已經改寫過的父類別內容,就得用 super 關鍵字來呼叫。


所以想得到繼承機制是怎麼運作的嗎? Python 直譯器會先建立父類別的物件,然後才去建立子類別的物件,當子類別物件需要用到父類別的屬性跟方法時,就會向父類別物件提出要求使用,基本上父類別跟子類別的物件都是同時存在的。


「繼承」這個詞是社群沿用已久的詞,所以這裡仍是把 inheritance 當「繼承」來講,實際上中文的「繼承」隱含某人已死的概念,可是物件導向中完全沒有這個意思。事實是 inherit 僅僅是某物得到某物的意思,所以生物學的「遺傳」也是用 inherit 這個詞。


至於多型的概念有很多,最狹義的不外是因為繼承關係,像是 BC 兩個類別繼承自 ABC 能夠使用 A 相同名稱的方法。好比設計一個動物遊戲, Animal 作為所有動物的父類別,另設計 DogCat 繼承 Animal 等動物類別,每種動物都可以使用 hello() 方法,只是每一種動物說 hello() 的方式都可以不一樣。


簡單講,進入 Python 世界就是物件的世界, Python 中所有的一切都是物件,就算是數字 12.2 也是物件,其他像字串 (string) 、串列 (list) 、字典 (dictionary) 等也當然都是物件的一種。


the end


Python 新手之旅 V1.10 (Google Play)

沒有留言: