
物件導向程式設計 (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)
沒有留言:
張貼留言