文章首發於 微信公眾號:Python程式設計時光
這個標題「靜態方法其實暗藏玄機」其實只是該文章的一個知識點。或許有些標題黨,但沒有關係,我相信有不少人對此並沒有深入研究他們,不信我問你三個問題,你看能否答上來。
1、Python2.x和3.x中,函式和方法的區分有什麼不同?
2、有了類/例項方法和普通函式,為什麼還會有靜態方法?
3、Python3.x 中,靜態方法有幾種寫法?
帶著這三個問題,你可以嘗試在下文中尋找答案。
在 Python 2 中的函式和方法的區別,十分清晰,很好分辨。但在 Python3中,我卻發現完全又是另一套準則。
首先先來 Python2 的(以下在 Python2.7中測試通過)
可以得出結論:
1、普通函式(未定位在類裡),都是函式。
2、靜態方法(@staticmethod),都是函式。
3、類方法(@classmethod),都是方法。
4、例項方法(首引數為self且非靜態、非類方法的),都是方法。
你一定想說,類方法和例項方法,是方法沒錯呀,畢竟名字本身就有方法,普通函式是函式,我也理解呀。那靜態方法,為什麼不是方法而是函式呢?
名字只是一個外在的表面稱呼,你能說「趙鐵男」就一定是漢子嗎?
我的理解是:方法是一種和物件(例項或者類)繫結後的函式。
類方法的首參是cls
,呼叫時,無需顯示傳入。例項方法首參是self,呼叫時,也無需顯示傳入。
而靜態方法,其實和普通函式沒啥區別,唯一的區別就是,他定義的位置被放在了類裡,做為類或者例項的一個函式。
那你肯定又要問了,既然靜態方法和普通函式都是一樣的,為什麼要刻意將它放在類裡呢?
我上面說了,放在類裡定義,就可以讓它成為類的一個工具函式,這就像你身上隨身攜帶了一把刀,這把刀與你本人並沒有什麼直接關係,唯一的聯絡就是這把刀是你的,而你把它帶在身上,無論你去到哪裡,只要需要,你就可以直接拿出來用上。對比將函式放在類外面,缺點是什麼呢?就是當你出門在外(在別的模組裡)發現你要用刀的時候,還要特地跑一趟去商店買一把刀(import 這個函式)。
另外,我覺得靜態方法在業務和設計上的意義,會更多一些。
一般靜態方法是做為類或者例項的一個工具函式,比如對變數的做一個合法性的檢驗,對資料進行序列化反序列化操作等等。
說完了 Python2 ,再來說說Python3.
以前我覺得 Python2 對於方法和函式的界線更加清晰,但接觸了 Python3,我反而覺得Python3裡方法和函式的區分似乎更加合理。
還是剛剛那段程式碼,我更改了直譯器為Python3.6(以下在 Python3.6中測試通過)
和Python2的唯一區別是,People.jump
在Python3 中變成了函式。
這一下顛覆了你剛剛才建立起來的知識體系有木有?
先別急,我再做個試驗,也許你就知道了。
在 Python2中
執行People.jump('hello'),會報錯說,jump的首參必須為People的例項物件,這可以理解,畢竟jump定義時,第一個引數為self。
在 Python3中
你可以發現,這裡的jump的首參不再要求是 People 的一個例項,而可以是任意的物件,比如我使用字串物件,也沒有報錯。
也就是說,當你往jump中傳入的首參為People的例項時,jump 就是方法,而當你傳入的首參不是People的例項物件時,jump就是函式。
你看,多麼靈活呀。
再總結一下,在Python3中:
1、普通函式(未定位在類裡),都是函式。
2、靜態方法(@staticmethod),都是函式。
3、類方法(@classmethod),都是方法。
4、方法和函式區分沒有那麼明確,而是更加靈活了,一個函式有可能是方法也有可能是函式。
你肯定又要問了,那這是不是就意味著,Python3 中靜態方法,可以不用再使用@staticmethod 裝飾了呢,反正Python3都可以識別。
這是個好問題,是的,可以不用指定,但是最好指定,如果你不指定,你呼叫這個方法只能通過People.jump,而不能通過 self.jump了,因為首參不是 self,而如果使用@staticmethod 就可以使用self.jump。
所以說這是一個規範,就像類的私有方法,規範要求外部最好不要呼叫,但這不是強制要求,不是說外部就不能呼叫。
寫這篇文章的起源,是前兩天有位讀者在交流裡問到了相關的問題,正好沒什麼主題可以寫,就拿過來做為素材整理一下,也正好沒有寫過靜態方法、類方法的內容,沒想到簡單的東西,也能寫出這麼多的內容出來。