针对Python 实例方法、类方法和静态方法的详解
1. 概覽
先定義一個最簡單的 Python 3 的類:
class MyClass:def method(self):print('我是實例方法', self)@classmethoddef classmethod(cls):print('我是類方法', cls)@staticmethoddef 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 參數,該實例的類未必是定義類方法的類。如下例:
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' # 父類 class Animal:@classmethoddef classmethod(cls):print('cls是:' + str(cls.__name__))# 子類 class Dog(Animal):passdog = Dog() dog.classmethod() """ cls是:Dog """ # 注意不是類方法的定義類:Animal # 而是實例的所屬類:Dog2.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. 使用類方法實現披薩工廠
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Pizza:def __init__(self, ingredients):self.ingredients = ingredientsdef __repr__(self):return f'披薩({self.ingredients})'@classmethoddef margherita(cls):return cls(['馬蘇里拉奶酪', '番茄'])@classmethoddef 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 類。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import mathclass Pizza:def __init__(self, radius, ingredients):self.radius = radiusself.ingredients = ingredientsdef __repr__(self):return (f'披薩({self.radius!r}),'f'{self.ingredients!r})')def area(self):return self.circle_area(self.radius)@staticmethoddef 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 裝飾器。
類方法和靜態方法,從某種程度上傳達了類的設計意圖,使代碼易于維護。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的针对Python 实例方法、类方法和静态方法的详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速使用Python连接MySQL数据库
- 下一篇: Python 一个判断对象是否是一个已知