開發時難免會需要許多邏輯,為了讓程式碼好閱讀及好整理,通常我們會整理成方法
今天會來介紹 Python 中的幾種方法
- 實體方法 Instance Method
- 類別方法 Class Method
- 靜態方法 Static Method
理解這些方法以前,
我們先來看看什麼是 「 Class 類別 」、「 Instance 實體 」
Python 為物件導向語言 (Object Oriented Programming)
也就是說,在 Python 的世界中,幾乎所有的東西都是物件,
物件
會有 狀態 (state)
與 行為 (behavior)
包括我們待會會討論到的 類別
與 實體
,在 Python 中也都是物件
Class 類別
在 Python 中,Class 類別就像是模型
我們可以依照模型來製造許多的成品(就是所謂的 Instance 實體)
Instance 實體
從類別中產生出來的物件,會保有類別的 狀態
與 行為
function 方法
在 Python 中,方法也是物件
現在我們就來看看方法的種類吧
Instance Method 實體方法
看名字就像是實體專用的方法,不過真的是這樣嗎?
在 Python 中,當實體在使用方法的時候,會將自己做為方法的引數來呼叫,我們來看看例子
當 ning
作為 getting_old
的 receiver 的時候,
會把自己作為引數來做呼叫
1 | class Person: |
這時候的 getting_old
會是一個 bound method
1 | class Person: |
而 bound method
為 method object
的實體
據 Python 官方手冊對於 method object
的解釋:
當實體在呼叫該方法時,它會將實體作為第一個引數,並進行呼叫
the special thing about methods is that the instance object is passed as the first argument of the function.
1 | print(ning.getting_old.__class__) // <class 'method'> |
那如果改成類別呢?
此時 getting_old
會是一個 function 物件
1 | class Person: |
不過這時候就會出現錯誤訊息,因為 getting_old
必須要有一個參數(self)
1 | class Person: |
為什麼同樣的方法,不同的 receiver 會變成不同的物件呢?
當物件找到方法後,會透過一個叫做 __get__
的方法來判斷物件是 類別
還是 實體
,並且回傳 method-wrapper
的實體
1 | print(ning.getting_old.__get__) // <method-wrapper '__get__' of function object at 0x10498cd60> |
接著我們來看一下 __init__
初始化樣子
1 | print(ning.getting_old.__init__) // <method-wrapper '__init__' of method object at 0x100af5ec0> |
在這邊我們就可以看到兩者的差異
用 ning(實體) 來存取方法,方法會被轉為 method object
,也就是我們剛剛看到的 bound method
用 Person(類別) 來存取方法,一樣會是 function object
再複習一下 bound method
為方法將屬於該物件,可想像成是將方法綁在該物件上,只有該物件能做使用,並且在呼叫時,會將物件本身作為引數帶入
A method is a function that “belongs to” an object.
而單純的 function object,即是指沒有被綁定在物件上、不屬於任何物件的 function
Class Method 類別方法
除了實體方法,Python 中也有類別方法,聽起來也像是專門給類別使用的
不過說穿了,類別方法也只是一般的 function object 而已
用類別來呼叫方法時,會印出預期中的結果
1 | class Person: |
這時候的 show_method
就只是一般的方法
1 | class Person: |
有實驗精神的我們,用 __init__
來看一下
的確是一般的 function object
1 | print(Person.show_method.__init__) // <method-wrapper '__init__' of function object at 0x102364cc0> |
如果換成是實體來作為 receiver 呢?
首先在 __get__
之後就被轉為 method object
(也就是 bound method
)
將方法綁在 ning 身上了
1 | ning = Person(age=17) |
1 | class Person: |
但由於 bound method
是需要將物件本身當作引數傳進 function 並呼叫,
類別方法並沒有任何參數,因此引發錯誤
1 | class Person: |
實體方法與類別方法
其實實體方法與類別方法,本質上都是單純的 function,甚至跟有沒有參數無關
差別在於存取及呼叫的時候:
如果 receiver 是實體,就會轉成 method object
,將方法綁定在物件上,並將物件作為引數帶入呼叫
如果 receiver 是類別,依舊為一般的 function object
所以並沒有實體不能使用類別方法,類別不能使用實體方法的規則,只是因為在呼叫時期的動作不同,就會引發錯誤
@classmethod
由於版本相容性的問題 Python3 推出了一個裝飾器 @classmethod
(忘記裝飾器可參考 PYTHON - 函式裝飾器 FUNCTION DECORATOR
必須要使用 cls
作為參數帶入,這時候不管是實體還是類別,
存取的時候都變成了 bound method
1 | class Person: |
而實體跟類別都可以做使用
A class method can be called either on the class (such as C.f()) or on an instance (such as C().f()).
當實體呼叫的時候,實體會被忽略,實際上是該實體的類別在呼叫,
所以實體呼叫時也不會有錯誤訊息,因為會去找該實體的類別
Static Method 靜態方法
靜態方法不需要傳遞 類別 或者 實體 做為參數進去 function
通常沒有涉及到更改 類別 或 實體 的 attribute ,就可以用靜態方法來做
例如今天要計算傳進來的參數 * 100 等於多少,就很適合用靜態方法
(當然要傳 類別 或 實體 的 attribute 進去也可以,不過比起靜態方法,實體方法或類別方法可能更適合)
Python3 有提供靜態方法的裝飾器 @staticmethod
裝上去即可使用
1 | class Person: |
參考資料:
https://docs.python.org/3/tutorial/classes.html
https://realpython.com/instance-class-and-static-methods-demystified/