# 類別與物件 ###### tags: `python` ### 類別(Class) 類別就像一個設計圖或是輪廓。 例如要生產一輛汽車時,都會有設計圖,生產時依照這個決定汽車長什麼樣子以及有什麼功能。 類別(Class)就類似設計圖,用來定義產生物件(Object)後,該物件會有什麼屬性(Attribute)和方法(Method)。 #### 定義類別 首先會有class關鍵字,接著自定類別名稱,最後加上冒號。類別名稱的命名原則習慣上單字第一個字母大寫,每個相連的不同的單字的第一個字母也都為大寫。 ``` # 定義Car類別 class Car: pass ``` ### 物件(Object) 透過類別(Class)建立的實體稱為物件(Object),就像實際生產出來的汽車(例如:Benz、BMW)。 ``` myCar = Car() # 透過Car類別建立myCar實體 ``` ### 屬性(Attribute) 用來存放物件的資料。 建立物件的屬性語法如下: ``` object_name.attribute_name = value ``` 建立屬性範例: ``` myCar = Cars() # 建立Cars類別的物件 myCar.color = "white" #顏色屬性 maCar.gas = 100 # 設定油量 ``` 存取物件的屬性值則透過以下語法: ``` object_name.attribute_name ``` 存取範例: ``` print(myCar.color) # 執行結果:white print(myCar.gas) # 執行結果:100 ``` ### 方法(Method) 為物件提供的功能。 定義方法和定義函式一樣都是def關鍵字開頭,接著自訂名稱,通常第一個單子的首字母為小寫,且方法必須有self參數(第一個參數),語法如下: ``` def method_name(self, param...):   statement ``` 範例: ``` class Car: def fillGas(self, gas): self.gas = gas myCar = Car() myCar.fillGas(200) print(myCar.gas) ``` > 在類別內所有的方法第一個參數都是`self`,我們並不需要自己傳入self參數,直譯器會自動幫我們帶入,只需要從第二個參數開始傳入即可。 ### 建構式(Constructor) #### 定義`__init__()`方法 `__init()`方法可以將該類別的初始化流程都定義在裡面,該方法在建立類別物件的時候會自動被呼叫。 ``` class Car: def __init__(self, color, gas): self.color = color self.gas = gas def show(self): print(self.color) print(self.gas) myCar = Car("white", 100) myCar.show() ``` > 請注意`__init__()`方法前後都是兩個底線。 ### 特殊物件方法 #### `__str()__`方法 透過定義`__str__()`方法,可以在該物件透過呼叫`str()`函式時輸出自訂的字串,例如: ``` class Car: ... 程式碼同上個範例,省略 ... def __str__(self): return 'Color=' + self.color + ', gas=' + str(self.gas) myCar = Car("white", 100) print(str(myCar)) ``` > 請注意,該方法一定要回傳一個字串。 ### 繼承 有些類別會有一些相關的程式碼,例如: ``` class Bus: def __init__(self, color, gas): self.color = color self.gas = gas def run(self): print('巴士開動') def showStatus(self): print(f'車子顏色為:{self.color}色') print(f'車子汽油量:{self.gas}公升') class Truck: def __init__(self, color, gas): self.color = color self.gas = gas def run(self): print('卡車開動') def showStatus(self): print(f'車子顏色為:{self.color}色') print(f'車子汽油量:{self.gas}公升') bus = Bus('白', 100) truck = Truck('藍', 200) bus.run() bus.showStatus() truck.run() truck.showStatus() ``` 其建構式和showStatus()方法其實程式碼都是一樣的,因此我們可以將之提取到一個類別Car: ``` class Car: def __init__(self, color, gas): self.color = color self.gas = gas def showStatus(self): print(f'車子顏色為:{self.color}色') print(f'車子汽油量:{self.gas}公升') ``` 然後讓Bus和Truck類別都繼承自Car類別: ``` class Bus(Car): def run(self): print('巴士開動') class Truck(Car): def run(self): print('卡車開動') ``` 繼承的方式也就是在類別名稱定義後面多一對小括號,並且將要繼承的類別名稱放在裡面,該Car類別也稱之為==父類別==;因為Bus和Truck都繼承自Car類別,因此Car類別內的方法,在Bus和Truck類別內都可以呼叫使用,Bus和Truck類別也稱之為子類別,例如: ``` bus.run() bus.showStatus() truck.run() truck.showStatus() ``` 這樣一來,如果未來showStatus()方法內的邏輯需要改變,只要修改服類別即可,而不用每個類別的方法都要修一次。 ### 方法覆載 當子類別內的方法和父類別相同時,子類別的方法將會覆蓋掉父類別的方法,例如: ``` class ParentClass: def show(self): print('我是父類別') class ChildClass(ParentClass): def show(self): print('我是子類別') c = ChildClass() c.show() ``` 如果在子類別內想呼叫父類別的show()方法,可以使用super()方法,例如: ``` super().show() ``` 就可以呼叫父類別的show()方法。 ### 其他相關 #### 判斷類別與物件的關係:isinstance() Python提供了一個函式isinstance()來判斷類別(Class)與物件(Object)的關係,語法如下: ``` isinstance(object_name, class_name) ``` 範例: ``` # 汽車類別 class Car: pass # 機車類別 class Motorcycle: pass # 建立Cars類別的物件 myCar = Car() print(isinstance(myCar, Car)) # 執行結果:True print(isinstance(myCar, Motorcycle)) # 執行結果:False ``` > 由於myCar並不是Motorcycle類別產生的物件實體,所以執行結果為False。 ## 進階 ### 實體方法(Instance Method) 最基本的類別中的方法,其條件為: 1. 該方法沒有加任何裝飾詞(Decorator)。 2. 至少有一個self參數,該方法被呼叫時指向物件(Object)。 範例: ``` class Car: def fill(self, gas): print("fill是個實體方法.") ``` 在沒有實現`__str__`方法下,如果直接print(self),可以看到,self變數內存放的就是物件,例如: ``` <__main__.Car object at 0x112addee0> ``` ### 類別方法(Class Method) 特色為: 1. Python類別(Class)中有@classmethod裝飾詞(Decorator)的方法(Method)。 2. 被呼叫時,其方法第一個參數為class參數,指向類別(Class)。 ``` class Car: # 類別方法(Class Method) @classmethod def drive(cls): print("drive是一個類別方法.") ``` 由於類別方法(Class Method)的cls參數指向類別(Class),所以類別方法(Class Method)只能修改類別的屬性,而無法改變物件的屬性,因為它沒指向物件的self參數。 ``` class Car: maxSpeed = 4 # 類別屬性 # 類別方法(Class Method) @classmethod def drive(cls): print(f"{cls} max speed is {cls.maxSpeed}.") myCar = Car() myCar.drive() #透過物件呼叫 Car.drive() #透過類別呼叫 ``` 執行結果: ``` <class '__main__.Car'> max speed is 4. <class '__main__.Car'> max speed is 4. ``` ##### 類別方法(Class Method)在Python裡常應用於產生物件,例如: ``` class Car: # 建構式 def __init__(self, maxGas, seat): self.maxGas = maxGas self.seat = seat # 跑車 @classmethod def sportCar(cls): return cls(100, 2) # 廂型車 @classmethod def vans(cls): return cls(300, 6) myFirstCar = Car.sportCar() mySecondCar = Car.vans() ``` > 你也可以用 `myFirstCar = Car(100, 2)`來產生一輛跑車實體物件,但當你需要在很多地方都產生跑車實體時,用類別方法來產生實體的做法會比較簡潔且不容易出錯。 ### 靜態方法(Static Method) 特色為: 1. 使用`@staticmethod`裝飾詞(Decorator)的方法(Method)。 2. 沒有self及cls參數。 範例: ``` class Car: #靜態方法 @staticmethod def show(): print("這是一個靜態方法.") ``` > 靜態方法在類別中是一個獨立的方法,通常應用於方法中不需要存取物件及類別的的屬性或方法的情境,它可以透過物件或是類別來呼叫。 ##### 透過物件呼叫 ``` myCar = Car() myCar.show() ``` ##### 透過類別呼叫 ``` Car.show() ``` 執行結果 > 1. 不論透過類別或物件都可以呼叫,執行期間不會傳入self及cls參數。 > 2. 一般會用在單元的測試。 > 3. 因為參數不會有self或cls,所以沒有辦法存取類別或物件屬性。