# Python的物件與類別 ## Python以物件(object)的方式看待、處理資料 Python是一種`物件導向程式語言`,從資料、變數到函式,都被視為「物件」(Object)來處理,物件包含以下元素 - `名稱`:用來稱呼這個物件 - `資料型態`: 設定物件的用途 - `唯一的id`: 用來區別不同物件 - `值`:真正的資料內容 - `參考計數`: 追蹤這物件被使用的頻率 例如宣告一個變數`x = 1`,在Python的設計觀點: - 使用者宣告了一個`變數物件`,賦予`名稱`為`x` - 系統賦予一個識別用的id - 這個物件的資料值指定為`1` - 因為存放數值`1`,此物件資料型態是`integer` 以上是Python內建的資料型別變數物件,在這個章節中,我們將介紹,使用者怎麼自訂物件,處理客製化資料。 ## 什麼是物件 `物件(object)`是一種資料型態,由`屬性(Attribute)`與`方法(Method)`組成。 屬性以**變數**形式表示,一個物件可以包含多個屬性,例如一個多邊形物件的顏色、長度。 方法以**函式**來表示,一個物件可以包含多個函式,例如一個多邊形物件可以被計算面積、體積 ## 什麼是類別 `類別(Class)`,就是用來建立物件的方法。 你必須定義類別,來指出一個物件包含哪些屬性跟方法。 ### 定義類別 例如你想定義一個銀行帳號的物件 - 屬性包括帳號代碼(number)跟戶名(name)、跟餘額(balance) - 方法包括存款(deposit)與提款(withdraw) 參考語法如下。 ```python= class Account: def __init__(self, number, name): self.number = number self.name = name self.balance = 0 def deposit(self, amount): #存款動作: amount代表存入金額 if amount <= 0: print('must be positive') self.balance += amount def withdraw(self, amount): #取款動作: amount代表取款金額 if amount <= self.balance: self.balance -= amount else: print('balance not enough') ``` ### 從類別中建立物件 通常也被稱為`實例(Instance)` 延續上面的程式碼,我們建立兩個帳號資料物件,分別是: - Account1 - number:`S00001` - name:`AAA` - Account2 - number:`S00002` - name: `BBB` 參考程式碼如下: ```python= class Account: def __init__(self, number, name): self.number = number self.name = name self.balance = 0 def deposit(self, amount): #存款動作: amount代表存入金額 if amount <= 0: print("Error!") self.balance += amount def withdraw(self, amount): #取款動作: amount代表取款金額 if amount <= self.balance: self.balance -= amount else: print("Error!") #建立account 1 account1 = Account("S00001", "AAA") account1.deposit(1000) #存入1000 print(account1.balance) #建立account 2 account2 = Account("S00002", "BBB") account2.deposit(2000) #存入2000 print(account2.balance) ``` ### 不預先定義屬性的作法 <a id="none_property" ></a> Python允許在物件生成階段,再進行屬性的定義,例如下方例子的第16, 17行。 ```python= class Account: pass def deposit(acct, amount): if amount <= 0: raise ValueError('must be positive') acct.balance += amount def withdraw(acct, amount): if amount <= acct.balance: acct.balance -= amount else: raise RuntimeError('balance not enough') acct = Account() acct.number = '123-456-789' acct.name = 'Justin' acct.balance = 0 print(acct.number) # 123-456-789 print(acct.name) # Justin deposit(acct, 100) print(acct.balance) # 100 withdraw(acct, 50) print(acct.balance) # 50 ``` ### 隨堂練習 :::info 請宣告一個class `CRectangle`(長方形類別) - 它的屬性包括長`width`跟寬`height` - 函式是`area(width, height)`,用來計算面積 請使用此class,生成一個長寬2x3的`rectangle1`物件,並且呼叫他的`area`方法,輸出它的面積 ::: :::spoiler 參考程式碼 ```python= class CRectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): area = self.width * self.height return area rectangle1 = CRectangle(2,3) rectangle1.area() print(rectangle1.area()) ``` ::: ### 類別的繼承 我們從這個例子出發,有兩個class,都有`drive`這個方法,根據物件導向設計原則,我們應該使用`繼承`這個技巧,來優化程式碼,方便後續維護。 ```python= # 汽車類別 class Car: # 駕駛方法 def drive(self): print("drive method is called.") # 加速方法 def accelerate(self): print("accelerate method is called.") # 飛機類別 class Airplane: # 駕駛方法 def drive(self): print("drive method is called.") # 飛行方法 def fly(self): print("fly method is called.") ``` 繼承作法如下,我們新增一個`class` Transpotation,也就是交通工工具的類別,然後讓原本的兩個`class`Car, Airplane繼承它,所以這兩個class都不用再實作自己的`drive`方法了。 ```python= # 交通工具(基底類別) class Transportation: # 建構式 def __init__(self): self.color = "white" #顏色屬性 # 駕駛方法 def drive(self): print("drive method is called.") # 汽車子類別 class Car(Transportation): # 加速方法 def accelerate(self): print("accelerate is method called.") # 飛機子類別 class Airplane(Transportation): # 飛行方法 def fly(self): print("fly method is called.") ``` ### 方法覆寫(Method Overriding) 當子類別中定義了和父類別同名的方法(Method),這時候子類別的物件(Object)呼叫這個同名方法時,其中的實作內容將會覆蓋掉父類別的同名方法,這就叫做方法覆寫(Method Overriding),如下範例: ```python= # 交通工具(基底類別) class Transportation: # 駕駛方法 def drive(self): print("Base class drive method is called.") # 汽車子類別 class Car(Transportation): # 駕駛方法 def drive(self): print("Sub class drive method is called.") car1 = Car() car1.drive() ``` 如果我們想在子類別中執行父類別的方法時,則可以使用`super()`內建方法來達成,如下範例: ```python= # 交通工具(基底類別) class Transportation: # 駕駛方法 def drive(self): print("Base class drive method is called.") # 汽車子類別 class Car(Transportation): # 駕駛方法 def drive(self): super().drive() print("Sub class drive method is called.") car1 = Car() car1.drive() ``` 從執行結果可以看到,子類別透過`super()`內建方法執行父類別的`drive()`方法後,接著執行子類別的方法後續實作。 ## 隨堂練習 :::info 請創造一個class,`CPolygon`多邊形 - 屬性有`line1`, `line2` - 方法有`area()`計算面積 再宣告兩個class,`CTriangle`與`CRectangle`,繼承`CPolygon`,各自實作自己的`area()`進行面積計算。 - `CTriangle.area()`: 三角形面積公式 - `CRectangle.area()`: 長方形面積公式 請使用以上兩個class,各自生成物件`triangle1(2,3)`和`rectangle1(2,3)`,並且使用它們的方法,計算面積並輸出。 ::: :::spoiler 參考程式碼 ```python= class CPolygon: def __init__(self, line1, line2): self.line1 = line1 self.line2 = line2 def area(self): print("area") class CTriangle(CPolygon): def area(self): area = (self.line1 * self.line2)/2 return area class CRectangle(CPolygon): def area(self): area = self.line1 * self.line2 return area triangle1 = CTriangle(2,3) print(triangle1.area()) rectangle1 = CRectangle(2,3) print(rectangle1.area()) ``` ::: ## 類別的進階使用技巧 - 多重繼承 - 屬性存取 - 聚合與組合 ###### tags: `Python程式設計入門`