# Python OOP (物件導向) ###### tags: `Python` * **OOP(Object-oriented programming, 物件導向程式設計):** 它是一個具有 **物件(Object)** 概念的開發方式,能夠提高軟體的**重用性、擴充性及維護性** * OOP三大特性: `封裝`、`繼承`、`多型` ### 封裝(encapsulation) >簡單來說:隱藏程式實現細節只保留下接口,使程式容易模組化。 簡單例子,無須知道work這個函式內部實作的細節,會用就好。 ```python class Worker: def __init__(self, name): self.name = name def work(self): print("I'm working now!! Don't bother me.") w = Worker('Madi') w.work() # I'm working now!! Don't bother me. ``` 變數/函式前面加上雙底線,ex: `__sleep`,會變成private member。 ### 繼承(inheritance) ```python class Worker: def __init__(self): self.salary = 100 class Madi(Worker): # Madi繼承 def __init__(self): self.salary = 200 # 子類別的變數 super().__init__() # super()代表父類別,__init__()會覆蓋掉子類別的self def withdraw(self): print(self.salary) # 父類別的變數,因為self被覆蓋掉 md = Madi() md.withdraw() #100 ``` ### 多型 >呼叫同名的方法時,會得到不同的結果。 ```python class Employee: def work(self): print('Employee work') class Andy(Employee): def work(self): print('Andy work') class Joy(Employee): def work(self): print('Joy work') w = Employee() w1 = Andy() w2 = Joy() w.work() # Employee work w1.work() # Andy work w2.work() # Joy work ``` ## Method(方法): * **實例方法(Instance Method)**: 帶有`self`(實例)為參數的方法 * **類別方法(Class Method)**: 帶有`cls`(類別)為參數的方法 * **靜態方法(Static Method)**: 不帶有`self`、`cls`為參數的方法 * **抽象方法(Abstract Method)**: 尚未被實作且 children class 一定要用 override來實作之,類同Java裏頭的interface(介面)。 以下整理至 [Python進階技巧 (2) — Static/Class/Abstract Methods之實現](https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-2-static-class-abstract-methods%E4%B9%8B%E5%AF%A6%E7%8F%BE-1e3b3998bccf) ### 實例方法(Instance Method) >在Python裡的instance method **必須傳入`self`參數** 以下為一個例子,建立一個Person的類別,裏頭有introduce這個instance method。 ```Python class Person: def __init__(self, name, age): self.name = name self.age = age # instance method def introduce(self): # 必須傳入self print(f"My name is {self.name}, I'm {self.age} old.") p = Person('Madi', 25) p.introduce() # My name is Madi, I'm 25 old. Person.introduce() # Error! 無法使用class call instance method ``` instance method 只能instance call,不能class call。 ### 靜態方法(Static Method) #### Q: 何謂 Static : >**一個 member 被宣告為 static 代表該 member 是在 Class Initialization 階段就被寫在一塊記憶體上**,因為是發生在 Instance Initialization 前,所以理所當然不會帶有 instance,且該 static member 之參數必須也是 static的(不然根本拿不到)。而**該 class 所生之所有 instances 皆可以使用該 static member**。 此外,Static 也可以在設計上與記憶體使用上帶來好處: 1. 將某個**member**設成static,**代表該 member獨立於instance,與instance無關,不帶instance為參數,使member間的關係乾淨俐落。** 2. 將某個**variable**設成static,**代表該class所生之所有instance都共用相同值的變數,作為instance之間的溝通。** 3. 承上,設成static的variable或member都**只會使用到一塊記憶體,不隨instance增加的數量而增加,減少記憶體使用。** >在Python裡使用`@staticmethod`這個decorator來實作static method。 同樣的例子,再增加一個static method叫breath,注意的是**不能傳入`self`參數。** ```Python class Person: def __init__(self, name, age): self.name = name self.age = age def introduce(self): print(f"My name is {self.name}, I'm {self.age} old.") @staticmethod def breath(): # 不能加self參數 print('breath...') p = Person('Madi', 25) Person.breath() # breath... p.breath() # breath... ``` static method 可以instance call,也可以class call。 ### 類別方法(Class Method) >在Python裡使用`@classmethod`這個decorator來實作class method。 同樣的例子,上述的Person這個類別再加一個get_weather的class method,使用class裏頭定義的weather這個變數,須注意的是,**必須傳入`cls`參數。** ```python class Person: weather = 'cloud' # cls裏頭的變數 def __init__(self, name, age): self.name = name self.age = age def introduce(self): print(f"My name is {self.name}, I'm {self.age} old.") @staticmethod def breath(): print('breath...') @classmethod def get_weather(cls): # 必須傳入cls參數 print(f"Today's weather is {cls.weather}.") # cls.變數名稱 p = Person('Madi', 25) Person.get_weather() # Today's weather is cloud. p.get_weather() # Today's weather is cloud. ``` 1. class method 可以instance call,也可以class call。 2. 與static method不同的是,class method可以用`cls`去access class內部的member。 ### 抽象方法(Abstract Method) >在Python裡要先import `abc`,再使用`@abc.abstractmethod`這個decorator來實作abstract method。 ```python import abc # 載入Abstract method(內建) class Person(metaclass=abc.ABCMeta): # 設定metaclass weather = 'cloud' def __init__(self, name, age): self.name = name self.age = age def introduce(self): print(f"My name is {self.name}, I'm {self.age} old.") @staticmethod def breath(): print('breath...') @classmethod def get_weather(cls): print(f"Today's weather is {cls.weather}.") @abc.abstractmethod def get_gender(self): return NotImplemented # 尚未實作,留給子類別實作,且子類別必須實作 # Man繼承Person class Man(Person): def get_gender(self): # override父類別的abstract method print("I'm a man.") m = Man('Eric', 25) Man('Eric', 25).get_gender() # I'm a man. m.get_gender() # I'm a man. ``` abstract method 可以instance call,也可以class call。