针对Python 实例方法、类方法和静态方法的详解
1. 概覽
先定義一個最簡單的 Python 3 的類:
class MyClass:def method(self):print('我是實例方法', self)@classmethoddef classmethod(cls):print('我是類方法', cls)@staticmethoddef staticmethod():print('我是靜態(tài)方法')1.1 實例方法
第一個方法 method(self) 方法是 實例方法 instance method 。 當 method 被調(diào)用時, self 參數(shù)指向 MyClass 類的一個實例。 實例方法可以通過 self 自由地訪問同一對象的屬性和其它方法,這樣它們可以修改實例的狀態(tài)。 注意實例方法可以通過 self.__class__ 屬性來獲取到類,所以實例方法也可以更改類的狀態(tài)。
1.2 類方法
第二個方法 classmethod(cls) 是 類方法 class method 。 上面需要寫一個 @classmethod 裝飾器。 類方法接收一個 cls 參數(shù),當該方法被調(diào)用的時候,它指向類(而不是類的實例)。 類方法只有 cls 參數(shù),所以它 不能 修改實例的狀態(tài)。 修改實例的狀態(tài)必須要有 self 參數(shù)。 類方法只能修改類的狀態(tài),類狀態(tài)的更改會作用于所有該類的實例。
1.3 靜態(tài)方法
第三個方法 staticmethod() 是 靜態(tài)方法 static method 。 它上面要有一個 @staticmethod 裝飾器。 靜態(tài)方法不能修改類或者實例的狀態(tài),它受限于它所接收的參數(shù)。 我們一般用這種方法來隔離命名空間。
2. 實際應(yīng)用
2.1 調(diào)用實例方法
首先創(chuàng)建一個實例,然后調(diào)用一下實例方法:
obj = MyClass()# 調(diào)用實例方法 obj.method() """ 我是實例方法 <__main__.MyClass object at 0x00000213E209B898> """還可以這樣調(diào)用:
MyClass.method(obj) """ 我是實例方法 <__main__.MyClass object at 0x00000213E209B898> """使用 對象.實例方法() 這種點號調(diào)用的形式是一個語法糖, Python 會自動把 對象 作為第一個實參,傳遞給 實例方法 中的 self 形參。 如果使用 類.實例方法(對象) 這種形式,則必須手動傳遞 對象 給 實例方法 的第一個參數(shù) self 。
如果不創(chuàng)建實例就調(diào)用實例方法,或者是不傳入 對象 ,那么就會出錯:
# 不創(chuàng)建實例就調(diào)用實例方法會發(fā)生什么? # 會提示缺少位置參數(shù) 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 調(diào)用類方法
下面來調(diào)用一下類方法。
# 通過類名調(diào)用類方法 MyClass.classmethod() # 也會自動傳遞類名作為第一個參數(shù) """ 我是類方法 <class '__main__.MyClass'> """通過 類.類方法() 的形式調(diào)用類方法, Python 會自動把 類 作為第一個參數(shù)傳遞給 類方法 的第一個參數(shù) cls ,我們不用手動傳遞。
也可以用實例調(diào)用類方法:
# 當然也可以通過實例調(diào)用類方法 obj.classmethod() """ 我是類方法 <class '__main__.MyClass'> """通過實例調(diào)用類方法, Python 會把該實例的類傳遞給 類方法 的 cls 參數(shù),該實例的類未必是定義類方法的類。如下例:
''' 學(xué)習(xí)中遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和PDF電子書! ''' # 父類 class Animal:@classmethoddef classmethod(cls):print('cls是:' + str(cls.__name__))# 子類 class Dog(Animal):passdog = Dog() dog.classmethod() """ cls是:Dog """ # 注意不是類方法的定義類:Animal # 而是實例的所屬類:Dog2.3 調(diào)用靜態(tài)方法
最后調(diào)用一下靜態(tài)方法:
# 調(diào)用靜態(tài)方法 obj.staticmethod() """ 我是靜態(tài)方法 """ # 調(diào)用靜態(tài)方法的時候 # 點號語法不會自動傳遞任何參數(shù)通過 實例.靜態(tài)方法() 調(diào)用靜態(tài)方法的時候, Python 不會傳遞 self 和 cls ,以此來限制靜態(tài)方法的權(quán)限。所以靜態(tài)方法不能獲取實例或者類的狀態(tài)。 它們就像普通函數(shù)一樣,只不過隸屬于類和該類的每個實例的命名空間。
2.4 不創(chuàng)建實例調(diào)用方法
不創(chuàng)建實例,調(diào)用實例方法、類方法和靜態(tài)方法。
# 不創(chuàng)建實例,調(diào)用類方法 MyClass.classmethod() """ 我是類方法 <class '__main__.MyClass'> """# 不創(chuàng)建實例,調(diào)用靜態(tài)方法 MyClass.staticmethod() """ 我是靜態(tài)方法 """# 不創(chuàng)建實例,調(diào)用實例方法 MyClass.method() """ Traceback (most recent call last):File "test.py", line 85, in <module>MyClass.method() TypeError: method() missing 1 required positional argument: 'self' """不創(chuàng)建實例,調(diào)用實例方法出錯。 這是可以理解的,因為我們直接通過類這個藍圖 blueprint 本身來調(diào)用實例方法, Python 無法給 self 傳參。
3. 使用類方法實現(xiàn)披薩工廠
''' 學(xué)習(xí)中遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和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(['馬蘇里拉奶酪', '番茄', '火腿'])使用類方法作為工廠函數(shù),生產(chǎn)不同種類的披薩。
【注】 工廠函數(shù) factory function 工廠函數(shù)是一個函數(shù),它根據(jù)不同的輸入,新建并返回不同的對象。
注意在工廠函數(shù)中,沒有直接使用 Pizza 這個類名,而是使用了 cls 這個參數(shù)。 這樣的好處在于易于維護。 萬一以后要把 Pizza 這個類名改成 披薩 ,只改動一處就行,因為類方法中用的是 cls 而不是直接寫 類名 。 這是遵循 DRY 原則的一個小技巧( Don’t repeat yourself )
現(xiàn)在使用工廠函數(shù)來生成幾個披薩吧:
pizza1 = Pizza.margherita() print(pizza1) """ 披薩(['馬蘇里拉奶酪', '番茄']) """pizza2 = Pizza.prosciutto() print(pizza2) """ 披薩(['馬蘇里拉奶酪', '番茄', '火腿']) """我們可以使用工廠函數(shù)來創(chuàng)建事先配置好的 Pizza 對象。 這些工廠函數(shù)內(nèi)部都使用了 init 構(gòu)造函數(shù),它們提供了一個捷徑,不用記憶各種披薩配方。 從另外一個角度來看,這些 類方法可以為一個類定義多個構(gòu)造函數(shù) 。
4. 何時使用靜態(tài)方法
改寫上面寫的 Pizza 類。
''' 學(xué)習(xí)中遇到問題沒人解答?小編創(chuàng)建了一個Python學(xué)習(xí)交流QQ群:531509025 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學(xué)習(xí)教程和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試一試使用靜態(tài)方法:
# 生成披薩 p = Pizza(4, ['馬蘇里拉奶酪', '番茄']) print(p) """ 披薩(4,['馬蘇里拉奶酪', '番茄']) """# 計算披薩的面積 p.area() print(p.area()) """ 50.26548245743669 """# 通過類調(diào)用靜態(tài)方法 print(Pizza.circle_area(4)) """ 50.26548245743669 """# 通過對象調(diào)用靜態(tài)方法 print(p.circle_area(4)) """ 50.26548245743669 """把一個方法寫成靜態(tài)方法的好處:
- 表明它不會更改類或者實例的狀態(tài)
- 更容易寫測試代碼,不用進行實例化就可以測試靜態(tài)方法
5. 總結(jié)
調(diào)用實例方法,需要一個實例。實例方法可以通過 self 來獲取實例。
self self類方法可以用實例或者類來調(diào)用。類方法可以通過 cls 獲取類本身。類方法上面要加 @classmethod 裝飾器。
- 通過實例調(diào)用類方法,不用手動傳類到 cls 。 通過實例調(diào)用的類方法, Python 自動傳遞到 cls 的類是該對象的所屬類,不一定是定義該類方法的類。(比如父類定義了類方法,子類繼承父類。通過子類的實例調(diào)用父類的類方法,傳到 cls 中的參數(shù)是子類,而不是定義類方法的父類。)
- 通過類調(diào)用類方法,也不用手動傳類到 cls 。
靜態(tài)方法可以用實例或者類調(diào)用。
靜態(tài)方法無法獲取到 cls 和 self 。 靜態(tài)方法上面要加 @staticmethod 裝飾器。
類方法和靜態(tài)方法,從某種程度上傳達了類的設(shè)計意圖,使代碼易于維護。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的针对Python 实例方法、类方法和静态方法的详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速使用Python连接MySQL数据库
- 下一篇: Python 一个判断对象是否是一个已知