一、實現方法
class Function(object): # 在類定義中定義變量 cls_variable = "class varibale" def __init__(self): # 在構造函數中創建變量 self.__instance_variable = "instance variable" def instance_method(self): print(self.cls_variable) print(self.__instance_variable) print("this is a instance method") @staticmethod def static_method(): print(Function.cls_variable) # print(Function.__instance_variable) 此處會報錯,無法訪問實例變量 print("this is a static method") @classmethod def class_method(cls): print(cls.cls_variable) # print(cls.__instance_variable) 此處會報錯,無法訪問實例變量 print("this is a class method") @classmethod def set_class_variable(cls): cls.cls_variable = 'new class variable' def set_instace_varibale(self): self.__instance_variable = 'new instance varibale' # 類實例可以調用類方法和靜態方法 function1 = Function() function1.set_class_variable() function1.class_method() function1.instance_method() function1.static_method() function2 = Function() function2.set_instace_varibale() function2.class_method() function2.instance_method() function2.static_method()
1、從代碼定義中,可以看到只是在默認傳入參數的不同。
Function.class_method() Function.static_method() # 可以調用實例函數,只不過需要傳入實例變量 Function.instance_method(function1)
2、從代碼訪問中,通過實例訪問這三種方法是一樣的。但是同時類訪問時,不一樣,實例函數需要傳入實例。
3、函數訪問變量中,有很大不同。
@classmethod def class_method(cls): print(cls.cls_variable) # print(cls.__instance_variable) 此處會報錯,無法訪問實例變量
在init函數定義的是實例變量,因為變量前綴添加了self。在類開始時定義的類變量,不需要添加前綴。
在變量訪問中,發現類函數和靜態函數是無法直接訪問實例變量的,因為在后續調用中,不知道是那個實例的。但是實例函數是可以訪問類變量的。
4、修改變量的范圍
new class variable this is a class method new class variable instance variable this is a instance method new class variable this is a static method
上圖是function1中輸出的結果。
new class variable this is a class method new class variable new instance varibale this is a instance method new class variable this is a static method new class variable
這是function2的結果,則class variable都變化了。
如果通過類方法修改變量,則所有實例中的類變量都會修改,這個類似靜態變量了
如果通過實例修改變量,只是修改對應的實例變量。
二、三者選擇原則
通過第一節的分析,我們得知三者的不同,在訪問權限和方式上,類方法和靜態方法有很多相同之處。與實例方法的區別就是看看這個方法是不是實例獨有的方法,或者需要訪問實例變量的。另一個不同就是在繼承上了。
1、類方法和靜態方法
# -*- coding: utf-8 -*- """ @Time : 2017/12/29 9:50 @Author:dongbl @Description: """ class Function(object): X = 1 Y = 2 @staticmethod def averag(*mixes): return sum(mixes) / len(mixes) @staticmethod def static_method(): # 通過function調用,如果類名修改,此處需要修改不太方便 return Function.averag(Function.X, Function.Y) @classmethod def class_method(cls): return cls.averag(cls.X, cls.Y) class Subclass(Function): X =3 Y = 5 @staticmethod def averag(*mixes): return sum(mixes) / 3 func = Subclass() print(func.static_method()) print(func.class_method())
1、調用方式不同,另一方面就是如果類名修改,函數也修改
2、繼承。這是兩者最大的不同
1.5 2.6666666666666665
上面是兩者的輸出。
子類的實例繼承了父類的static_method靜態方法,調用該方法,還是調用的父類的方法和類屬性。
子類的實例繼承了父類的class_method類方法,調用該方法,調用的是子類的方法和子類的類屬性。
這就是最大的不同,靜態方法在類沒有初始化,已經加載了,后續繼承和她就沒有關系了。同時靜態方法關鍵明確指明了調用了Function的方法了,所以就無法修改了。這是本質。
三、類方法和實例方法
類中最常用的方法是實例方法, 即通過通過實例作為第一個參數的方法。
如果現在我們想寫一些僅僅與類交互而不是和實例交互的方法會怎么樣呢?在Python2.2以后可以使用@classmethod裝飾器來創建類方法.
比如我們創建一個類:
class DateTest(): def __init__(self,year,month,day): self.year = year self.day = day self.month = month def print_date(self): print("{}:{}:{}".format(self.year,self.month,self.day))
如果用戶輸入的是“2017-12-02”這樣的字符,我們在調用類之前就需要調整一下。
這個設計角度可以理解每個實例都可以調用這個轉化函數。
# -*- coding: utf-8 -*- """ @Time : 2017/12/29 9:50 @Author:dongbl @Description: """ class DateTest(): def __init__(self,year,month,day): self.year = year self.day = day self.month = month def print_date(self): print("{}:{}:{}".format(self.year,self.month,self.day)) @classmethod def get_date(cls,string_date): year,month,day = map(int,string_date.split('-')) return cls(year,month,day) t = DateTest(2017,9,10) r = DateTest.get_date("2017-12-09") r.print_date() t.print_date()
classmethod主要用途是作為構造函數。
Python只有一個構造函數__new__,如果想要多種構造函數就很不方便。只能在new里面寫一堆if isinstance 。
有classmethod之后就可以用classmethod來寫不同的構造函數,比如:
dict.fromkeys
fractions.Fraction.from_decimal
inspect.Signature.from_function
python里面大部分classmethod最后都是 return cls(XXX), return XXX.__new__ ()之類的
staticmethod主要用途是限定namespace,也就是說這個函數雖然是個普通的function,但是它只有這個class會用到,不適合作為module level的function。這時候就把它作為staticmethod。
如果不考慮namespace的問題的話直接在module里面def function就行了。
四、參考文獻
2、知乎經典解答