您好,登錄后才能下訂單哦!
@classmethod與@staticmethod的區別是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
Python面向對象編程中,類中定義的方法可以是 @classmethod 裝飾的類方法,也可以是 @staticmethod 裝飾的靜態方法,用的最多的還是不帶裝飾器的實例方法,如果把這幾個方法放一塊,對初學者來說無疑是一頭霧水,那我們該如何正確地使用它們呢?
先來看一個簡單示例:
class A(object):
def m1(self, n):
print("self:", self)
@classmethod
def m2(cls, n):
print("cls:", cls)
@staticmethod
def m3(n):
pass
a = A()
a.m1(1) # self: <__main__.A object at 0x000001E596E41A90>
A.m2(1) # cls: <class '__main__.A'>
A.m3(1)
我在類中一共定義了3個方法,m1 是實例方法,第一個參數必須是 self(約定俗成的)。m2 是類方法,第一個參數必須是cls(同樣是約定俗成),m3 是靜態方法,參數根據業務需求定,可有可無。當程序運行時,大概發生了這么幾件事(結合下面的圖來看)。
第一步:代碼從第一行開始執行 class
命令,此時會創建一個類 A 對象(沒錯,類也是對象,一切皆對象嘛)同時初始化類里面的屬性和方法,記住,此刻實例對象還沒創建出來。
第二、三步:接著執行 a=A()
,系統自動調用類的構造器,構造出實例對象 a
第四步:接著調用 a.m1(1)
,m1 是實例方法,內部會自動把實例對象傳遞給 self 參數進行綁定,也就是說, self 和 a 指向的都是同一個實例對象。
第五步:調用A.m2(1)
時,python內部隱式地把類對象傳遞給 cls 參數,cls 和 A 都指向類對象。
嚴格意義上來說,左邊的都是變量名,是對象的引用,右邊才是真正的對像,為了描述方便,我直接把 a 稱為對象,你應該明白我說對象其實是它所引用右邊的那個真正的對象。
再來看看每個方法各有什么特性
print(A.m1)
# A.m1在py2中顯示為<unbound method A.m1>
<function A.m1 at 0x000002BF7FF9A488>
print(a.m1)
<bound method A.m1 of <__main__.A object at 0x000002BF7FFA2BE0>>
A.m1是一個還沒有綁定實例對象的方法,對于未綁定方法,調用 A.m1 時必須顯示地傳入一個實例對象進去,而 a.m1是已經綁定了實例的方法,python隱式地把對象傳遞給了self參數,所以不再手動傳遞參數,這是調用實例方法的過程。
A.m1(a, 1)
# 等價
a.m1(1)
如果未綁定的方法 A.m1 不傳實例對象給 self 時,就會報參數缺失錯誤,在 py3 與 py2 中,兩者報的錯誤不一致,python2 要求第一個參數self是實例對象,而python3中可以是任意對象。
A.m1(1)
TypeError: m1() missing 1 required positional argument: 'n'
print(A.m2)
<bound method A.m2 of <class '__main__.A'>>
print(a.m2)
<bound method A.m2 of <class '__main__.A'>>
m2是類方法,不管是 A.m2 還是 a.m2,都是已經自動綁定了類對象A的方法,對于后者,因為python可以通過實例對象a找到它所屬的類是A,找到A之后自動綁定到 cls。
A.m2(1)
# 等價
a.m2(1)
這使得我們可以在實例方法中通過使用 self.m2()這種方式來調用類方法和靜態方法。
def m1(self, n):
print("self:", self)
self.m2(n)
print(A.m3)
<function A.m3 at 0x000002BF7FF9A840>
print(a.m3)
<function A.m3 at 0x000002BF7FF9A840>
m3是類里面的一個靜態方法,跟普通函數沒什么區別,與類和實例都沒有所謂的綁定關系,它只不過是碰巧存在類中的一個函數而已。不論是通過類還是實例都可以引用該方法。
A.m3(1)
# 等價
a.m3(1)
以上就是幾個方法的基本介紹。現在把幾個基本的概念理清楚了,那么現在來說說幾個方法之間的使用場景以及他們之間的優缺點。
靜態方法的使用場景:
如果在方法中不需要訪問任何實例方法和屬性,純粹地通過傳入參數并返回數據的功能性方法,那么它就適合用靜態方法來定義,它節省了實例化對象的開銷成本,往往這種方法放在類外面的模塊層作為一個函數存在也是沒問題的,而放在類中,僅為這個類服務。
例如下面是微信公眾號開發中驗證微信簽名的一個例子,它沒有引用任何類或者實例相關的屬性和方法。
from hashlib import sha1
import tornado.web
class SignatureHandler(tornado.web.RequestHandler):
def get(self):
"""
根據簽名判斷請求是否來自微信
"""
if self._check_sign(TOKEN, timestamp, nonce, signature):
self.write(echostr)
else:
self.write("你不是微信發過來的請求")
@staticmethod
def _check_sign(token, timestamp, nonce, signature):
sign = [token, timestamp, nonce]
sign.sort()
sign = "".join(sign)
sign = sha1(sign).hexdigest()
return sign == signature
類方法的使用場景有:
作為工廠方法創建實例對象,例如內置模塊 datetime.date 類中就有大量使用類方法作為工廠方法,以此來創建date對象。
class date:
def __new__(cls, year, month=None, day=None):
self = object.__new__(cls)
self._year = year
self._month = month
self._day = day
return self
@classmethod
def fromtimestamp(cls, t):
y, m, d, * = _time.localtime(t)
return cls(y, m, d)
@classmethod
def today(cls):
t = _time.time()
return cls.fromtimestamp(t)
如果希望在方法裡面調用靜態類,那么把方法定義成類方法是合適的,因為要是定義成靜態方法,那么你就要顯示地引用類A,這對繼承來說可不是一件好事情。
class A: @staticmethod def m1() pass @staticmethod def m2(): A.m1() # bad @classmethod def m3(cls): cls.m1() # good
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。