Python 的例項方法、類方法和靜態方法
Python 的例項方法、類方法和靜態方法
轉載請註明出處:https://blog.csdn.net/jpch89/article/details/84442713
文章目錄
0. 參考資料
1. 概覽
先定義一個最簡單的 Python 3
的類:
class MyClass:
def method(self):
print('我是例項方法', self)
@classmethod
def classmethod(cls):
print('我是類方法', cls)
@staticmethod
def staticmethod():
print('我是靜態方法')
1.1 例項方法
第一個方法 method(self)
方法是例項方法 instance method
。
當 method
被呼叫時,self
引數指向 MyClass
類的一個例項。
例項方法可以通過 self
自由地訪問同一物件的屬性和其它方法,這樣它們可以修改例項的狀態。
注意例項方法可以通過 self.__class__
屬性來獲取到類,所以例項方法也可以更改類的狀態。
1.2 類方法
第二個方法 classmethod(cls)
是類方法 class method
上面需要寫一個
@classmethod
裝飾器。類方法接收一個
cls
引數,當該方法被呼叫的時候,它指向類(而不是類的例項)。類方法只有
cls
引數,所以它不能修改例項的狀態。修改例項的狀態必須要有
self
引數。類方法只能修改類的狀態,類狀態的更改會作用於所有該類的例項。
1.3 靜態方法
第三個方法 staticmethod()
是靜態方法 static method
。
它上面要有一個 @staticmethod
裝飾器。
靜態方法不能修改類或者例項的狀態,它受限於它所接收的引數。
我們一般用這種方法來隔離名稱空間。
2. 實際應用
2.1 呼叫例項方法
首先建立一個例項,然後呼叫一下例項方法:
obj = MyClass()
# 呼叫例項方法
obj.method()
"""
我是例項方法 <__main__.MyClass object at 0x00000213E209B898>
"""
還可以這樣呼叫:
MyClass.method(obj)
"""
我是例項方法 <__main__.MyClass object at 0x00000213E209B898>
"""
使用 物件.例項方法()
這種點號呼叫的形式是一個語法糖,Python
會自動把 物件
作為第一個實參,傳遞給 例項方法
中的 self
形參。
如果使用 類.例項方法(物件)
這種形式,則必須手動傳遞 物件
給 例項方法
的第一個引數 self
。
如果不建立例項就呼叫例項方法,或者是不傳入 物件
,那麼就會出錯:
# 不建立例項就呼叫例項方法會發生什麼?
# 會提示缺少位置引數 self
# 例項方法依賴於例項而存在
MyClass.method()
"""
Traceback (most recent call last):
File "test.py", line 28, in <module>
MyClass.method()
TypeError: method() missing 1 required positional argument: 'self'
"""
例項方法可以通過 self.__class__
訪問到類。
# 列印類名
print(obj.__class__.__name__)
"""
MyClass
"""
2.2 呼叫類方法
下面來呼叫一下類方法。
# 通過類名呼叫類方法
MyClass.classmethod()
# 也會自動傳遞類名作為第一個引數
"""
我是類方法 <class '__main__.MyClass'>
"""
通過 類.類方法()
的形式呼叫類方法,Python
會自動把 類
作為第一個引數傳遞給 類方法
的第一個引數 cls
,我們不用手動傳遞。
也可以用例項呼叫類方法:
# 當然也可以通過例項呼叫類方法
obj.classmethod()
"""
我是類方法 <class '__main__.MyClass'>
"""
通過例項呼叫類方法,Python
會把該例項的類傳遞給 類方法
的 cls
引數,該例項的類未必是定義類方法的類。如下例:
# 父類
class Animal:
@classmethod
def classmethod(cls):
print('cls是:' + str(cls.__name__))
# 子類
class Dog(Animal):
pass
dog = Dog()
dog.classmethod()
"""
cls是:Dog
"""
# 注意不是類方法的定義類:Animal
# 而是例項的所屬類:Dog
2.3 呼叫靜態方法
最後呼叫一下靜態方法:
# 呼叫靜態方法
obj.staticmethod()
"""
我是靜態方法
"""
# 呼叫靜態方法的時候
# 點號語法不會自動傳遞任何引數
通過 例項.靜態方法()
呼叫靜態方法的時候,Python
不會傳遞 self
和 cls
,以此來限制靜態方法的許可權。所以靜態方法不能獲取例項或者類的狀態。
它們就像普通函式一樣,只不過隸屬於類和該類的每個例項的名稱空間。
2.4 不建立例項呼叫方法
不建立例項,呼叫例項方法、類方法和靜態方法。
# 不建立例項,呼叫類方法
MyClass.classmethod()
"""
我是類方法 <class '__main__.MyClass'>
"""
# 不建立例項,呼叫靜態方法
MyClass.staticmethod()
"""
我是靜態方法
"""
# 不建立例項,呼叫例項方法
MyClass.method()
"""
Traceback (most recent call last):
File "test.py", line 85, in <module>
MyClass.method()
TypeError: method() missing 1 required positional argument: 'self'
"""
不建立例項,呼叫例項方法出錯。
這是可以理解的,因為我們直接通過類這個藍圖 blueprint
本身來呼叫例項方法,Python
無法給 self
傳參。
3. 使用類方法實現披薩工廠
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'披薩({self.ingredients})'
@classmethod
def margherita(cls):
return cls(['馬蘇裡拉乳酪', '番茄'])
@classmethod
def prosciutto(cls):
return cls(['馬蘇裡拉乳酪', '番茄', '火腿'])
使用類方法作為工廠函式,生產不同種類的披薩。
【注】
工廠函式factory function
工廠函式是一個函式,它根據不同的輸入,新建並返回不同的物件。
注意在工廠函式中,沒有直接使用 Pizza
這個類名,而是使用了 cls
這個引數。
這樣的好處在於易於維護。
萬一以後要把 Pizza
這個類名改成 披薩
,只改動一處就行,因為類方法中用的是 cls
而不是直接寫 類名
。
這是遵循 DRY
原則的一個小技巧(Don't repeat yourself
)
現在使用工廠函式來生成幾個披薩吧:
pizza1 = Pizza.margherita()
print(pizza1)
"""
披薩(['馬蘇裡拉乳酪', '番茄'])
"""
pizza2 = Pizza.prosciutto()
print(pizza2)
"""
披薩(['馬蘇裡拉乳酪', '番茄', '火腿'])
"""
我們可以使用工廠函式來建立事先配置好的 Pizza
物件。
這些工廠函式內部都使用了 __init__
建構函式,它們提供了一個捷徑,不用記憶各種披薩配方。
從另外一個角度來看,這些類方法可以為一個類定義多個建構函式。
4. 何時使用靜態方法
改寫上面寫的 Pizza
類。
import math
class Pizza:
def __init__(self, radius, ingredients):
self.radius = radius
self.ingredients = ingredients
def __repr__(self):
return (f'披薩({self.radius!r}),'
f'{self.ingredients!r})')
def area(self):
return self.circle_area(self.radius)
@staticmethod
def circle_area(r):
return r ** 2 * math.pi
試一試使用靜態方法:
# 生成披薩
p = Pizza(4, ['馬蘇裡拉乳酪', '番茄'])
print(p)
"""
披薩(4,['馬蘇裡拉乳酪', '番茄'])
"""
# 計算披薩的面積
p.area()
print(p.area())
"""
50.26548245743669
"""
# 通過類呼叫靜態方法
print(Pizza.circle_area(4))
"""
50.26548245743669
"""
# 通過物件呼叫靜態方法
print(p.circle_area(4))
"""
50.26548245743669
"""
把一個方法寫成靜態方法的好處:
- 表明它不會更改類或者例項的狀態
- 更容易寫測試程式碼,不用進行例項化就可以測試靜態方法
5. 總結
-
呼叫例項方法,需要一個例項。例項方法可以通過
self
來獲取例項。- 通過例項呼叫例項方法,不用手動傳例項到
self
。 - 通過類呼叫例項方法,需要手動傳例項到
self
。
- 通過例項呼叫例項方法,不用手動傳例項到
-
類方法可以用例項或者類來呼叫。類方法可以通過
cls
獲取類本身。類方法上面要加@classmethod
裝飾器。- 通過例項呼叫類方法,不用手動傳類到
cls
。
通過例項呼叫的類方法,Python
自動傳遞到cls
的類是該物件的所屬類,不一定是定義該類方法的類。(比如父類定義了類方法,子類繼承父類。通過子類的例項呼叫父類的類方法,傳到cls
中的引數是子類,而不是定義類方法的父類。) - 通過類呼叫類方法,也不用手動傳類到
cls
。
- 通過例項呼叫類方法,不用手動傳類到
-
靜態方法可以用例項或者類呼叫。
靜態方法無法獲取到cls
和self
。
靜態方法上面要加@staticmethod
裝飾器。 -
類方法和靜態方法,從某種程度上傳達了類的設計意圖,使程式碼易於維護。
完成於 2018.11.24