物件導向程式設計 (object-oriented programming) 是對真實世界的比擬,例如我們設計一個模擬租借腳踏車的系統,租腳踏車的是人,利用電子錢包扣款租借,因此「人」、「電子錢包」、「腳踏車」在這個系統中都會是獨立的物件 (object) 。
以下寫個簡單程式來模擬,如下
001 | class 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 | |
028 | class 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 | |
040 | class 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 | |
050 | someone = Person("John") |
051 | newWallet = ElectronWallet(someone, 1000) |
052 | someone.setWallet(newWallet) |
053 | newBike = RenterBike(someone) |
054 | someone.rentBike(newBike) |
「人」用類別 (class) Person 來模擬
001 | class 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 來模擬,如下
028 | class 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 來模擬,如下
040 | class 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 。
我們用以下程式碼模擬租借過程
050 | someone = Person("John") |
051 | newWallet = ElectronWallet(someone, 1000) |
052 | someone.setWallet(newWallet) |
053 | newBike = RenterBike(someone) |
054 | someone.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 中類別的屬性都是公開的,也就是建立物件後就能直接運用小數點存取屬性。
如果不想要某個類別中的屬性被任意存取,就需要將屬性封裝了。封裝是在屬性前加上兩條底線,例如
001 | class 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
001 | class SC: |
002 | a = 11 |
003 | |
004 | def do_something(): |
005 | print("Hello, SC!") |
繼承的寫法就是類別識別字後加上小括弧,然後小括弧中放父類別的名稱,例如以下的 SC2
001 | class SC2(SC): |
002 | b = 22 |
003 | |
004 | def do_something(): |
005 | print("Hello, SC2!") |
這樣 SC2 除了有屬性 b 跟方法 do_something() 之外,也會有屬性 a ,至於原本 SC 的 do_something() 則被 SC2 的 do_something() 改寫 (override) 了。
這個情況是說,子類別如果設置跟父類別相同名稱的屬性或方法,基本上會以子類別的版本為主,如果子類別想要使用已經改寫過的父類別內容,就得用 super 關鍵字來呼叫。
所以想得到繼承機制是怎麼運作的嗎? Python 直譯器會先建立父類別的物件,然後才去建立子類別的物件,當子類別物件需要用到父類別的屬性跟方法時,就會向父類別物件提出要求使用,基本上父類別跟子類別的物件都是同時存在的。
「繼承」這個詞是社群沿用已久的詞,所以這裡仍是把 inheritance 當「繼承」來講,實際上中文的「繼承」隱含某人已死的概念,可是物件導向中完全沒有這個意思。事實是 inherit 僅僅是某物得到某物的意思,所以生物學的「遺傳」也是用 inherit 這個詞。
至於多型的概念有很多,最狹義的不外是因為繼承關係,像是 B 與 C 兩個類別繼承自 A , B 與 C 能夠使用 A 相同名稱的方法。好比設計一個動物遊戲, Animal 作為所有動物的父類別,另設計 Dog 、 Cat 繼承 Animal 等動物類別,每種動物都可以使用 hello() 方法,只是每一種動物說 hello() 的方式都可以不一樣。
簡單講,進入 Python 世界就是物件的世界, Python 中所有的一切都是物件,就算是數字 1 、 2.2 也是物件,其他像字串 (string) 、串列 (list) 、字典 (dictionary) 等也當然都是物件的一種。
the end
Python 新手之旅 V1.10 (Google Play)
沒有留言:
張貼留言