寫在前面
寫博客的時候,我發現拖延症很嚴重,本來昨天要開始寫的,結果東看看,西翻翻,啥也沒落實下來。時間過去了,口袋里的收獲卻寥寥無幾。討厭這樣的自己。我要戒掉這個不好的毛病。
拖延症的底層原因之一是:不知如何下手
拖延症的底層原因之二是:每天都重復,疲了,累了
這里僅代表我自己
1..什么是classmethod
classmethod是用來指定一個類的方法為類方法
長的像下面這個樣子
class cc: @classmethod def f(cls, arg1, arg2, ...): ...
cls通常用作類方法的第一參數 跟self有點類似( __init__里面的slef通常用作實例方法的第一參數)。即通常用self來傳遞當前類對象的實例,cls傳遞當前類對象。
self 和cls 沒有特別的含義,作用只是把參數綁定到普通的函數上, 不一定非得是slef 或cls , 可以換成別的xxx
2..為什么會出現classmethod
(下面的比較瑣碎,也不講章法,但是這些點點滴滴我想記錄下來,因為他們幫助我拼湊了這類知識的框架體系。我很喜歡用拼湊這個詞,每次碰到一個完全陌生的鬼,總是這兒點點,那兒翻翻,每個足跡都會留下一點點碎片,慢慢的,逛的久了,再動手寫寫,基本上這一類內容在我腦子里漸漸形成個輪廓了,這個輪廓就是這些碎片拼湊出來的)
1--classmethod設計的目的是什么呢?事實上與Python面向對象編程有關的,由於Python不支持多個的參數重載構造函數,比方在C++里,構造函數能夠依據參數個數不一樣。能夠寫多個構造函數。Python為了解決問題,採用classmethod修飾符的方式,這樣定義出來的函數就能夠在類對象實例化之前調用這些函數,就相當於多個構造函數,解決多個構造函數的代碼寫在類外面的問題。
2---類最基本的作用是實例化出一個對象,但是有的時候再實例化之前,就需要先和類做一定的交互,這種交互可能會影響實際實例化的過程,所以必須放在調用構造函數之前。大概也可能是因為這個原因出現了classmethod
3---直接一點來說,我們知道對於一個普通的類,我們要使用其中的函數的話,需要對類進行實例化,而一個類中,某個函數前面加上了staticmethod或者classmethod的話,那么這個函數就可以不通過實例化直接調用,
可以通過類名進行調用的
4---@classmethod 定義的類方法是可選構造函數中,我們定義了一個類方法,類方法的第一個參數(cls)指代的就是類本身。類方法會用這個類來創建並返回最終的實例。使用類方法的另一個好處就是在繼承的時候,保證了子類使用可選構造函數構造出來的類是子類的實例而不是父類的實例。
3..classmethod有什么用
在Java等語言中,這類功能通常通過工廠類(Factory)實現,先初始化一個工廠類的實例,然后由這個工廠類的實例構造實際需要的實例。在Python中,普通類完全可以替代Factory,而對於支持配置的Factory,就對應到相應的classmethod。
4..舉第一個例子來理解classmethod
(因為我功力太淺,我真的沒能理解@classmethod所起到的作用,但是知乎靈劍的這篇博客不明覺歷,我還是先把這個例子這,說不定哪一天就想通了)
1)有一個插件系統,可以自動從插件目錄中加載外部插件
2)Plugin是用戶自定義插件的基類,它的構造函數接受一個api_interface的參數,把它保存到實例中。當想用這個接口的時候,調callback就好了,達到擴展功能的目的
3)事情不可能一成不變,要升級了,需要重新設計插件系統
4)插件數量變更多了,每個插件只干自己的事(比如,比如縫紉機插件只管縫衣服的事情,蓋房子插件只管蓋房子的事,警察插件只管抓壞人的事),那希望安裝注冊插件的時候希望插件聲明好自己只干哪幾樣活(比如挖土插件要說好 :我只負責挖土,不負責縫衣服和蓋房子)。
5)api_interface也變了,但是希望舊接口不做任何改變就能直接用
6)舊插件也不做任何改動就可以繼續使用。
7)基於這樣的需求,我們需要在構造plugin對象之前,就先從類中獲取一定的信息。
5..再舉個例子來理解classmethod的使用方法
這個是CSDN的dyh4201,鏈接都放下面了,這個通俗易懂,總感覺@classmethod還能發揮更大的用途,只是我還沒完全參透,舉不出例子來
看下面的定義的一個時間類: class Data_test(object): day=0 month=0 year=0 def __init__(self,year=0,month=0,day=0): self.day=day self.month=month self.year=year def out_date(self): print "year :" print self.year print "month :" print self.month print "day :" print self.day t=Data_test(2016,8,1) t.out_date() 輸出: year : 2016 month : 8 day : 1 符合期望。 如果用戶輸入的是 "2016-8-1" 這樣的字符格式,那么就需要調用Date_test 類前做一下處理: string_date='2016-8-1' year,month,day=map(int,string_date.split('-')) s=Data_test(year,month,day) 先把‘2016-8-1’ 分解成 year,month,day 三個變量,然后轉成int,再調用Date_test(year,month,day)函數。 也很符合期望。 那我可不可以把這個字符串處理的函數放到 Date_test 類當中呢? 那么@classmethod 就開始出場了 class Data_test2(object): day=0 month=0 year=0 def __init__(self,year=0,month=0,day=0): self.day=day self.month=month self.year=year @classmethod def get_date(cls,data_as_string): #這里第一個參數是cls, 表示調用當前的類名 year,month,day=map(int,string_date.split('-')) date1=cls(year,month,day) #返回的是一個初始化后的類 return date1 def out_date(self): print "year :" print self.year print "month :" print self.month print "day :" print self.day 在Date_test類里面創建一個成員函數, 前面用了@classmethod裝飾。 它的作用就是有點像靜態類,比靜態類不一樣的就是它可以傳進來一個當前類作為第一個參數。 那么如何調用呢? r=Data_test2.get_date("2016-8-6") r.out_date() 輸出: year : 2016 month : 8 day : 1 這樣子等於先調用get_date()對字符串進行出來,然后才使用Data_test的構造函數初始化。 這樣的好處就是你以后重構類的時候不必要修改構造函數,只需要額外添加你要處理的函數,然后使用裝飾符 @classmethod 就可以了。
6.在繼承時也能工作的很好:
類方法的一個主要用途就是定義多個構造器。它接受一個class 作為第一個參數(cls)。在繼承時也能工作的很好:
7..還有其他待挖掘
關於@classmethod,以后要是發現新的用途或者有更深的理解了,我再繼續加到下面
參考
https://www.cnblogs.com/zfyouxi/p/5222811.html
http://30daydo.com/article/89
https://www.zhihu.com/question/20021164
https://blog.csdn.net/mr_hui_/article/details/82939979
https://www.cnblogs.com/agnewee/p/5653936.html