在python的類語法中,可以出現三種方法,具體如下:
(1)實例方法
1)第一個參數必須是實例本身,一般使用【self】表示。
2)在實例方法中,可以通過【self】來操作實例屬性,【類名】來操作類屬性。
3)實例方法只能通過實例對象去調用,盡管也可以通過類名間接調用【類名.方法名(self, ...)】,但此時仍然需要傳入self對象。
(2)類方法
1)使用【@classmethod】修飾函數,且第一個參數必須是類本身,一般使用【cls】表示。
2)在類方法中,可以使用【cls=類名】來操作類屬性,但是不能操作實例屬性(self對象中存儲的變量)。
3)類方法可以通過實例對象或者類對象調用。
(3)靜態方法
1)使用【@staticmethod】修飾函數,不需要使用任何參數表示。與普通函數一樣,只是將該方法放到了類中實現而已。
2)使用方式與類方法一樣,參考類方法中的 2)、3)。(注:靜態方法只能通過【類名】去操作類屬性;)
案例1:實例方法、類方法、靜態方法使用
class Foo(object): """類三種方法語法形式""" count = 0 # 統計實例對象的數量 class_method_count = 0 # 統計類方法的調用次數 # 實例方法 def __init__(self, name): self.name = name Foo.count += 1 # 實例方法 def instance_method(self): print("是類{}的實例方法,只能被實例對象調用".format(Foo)) print("產生了一個<{}>實例,共有<{}>個實例對象".format(self.name, Foo.count)) # 類方法 @classmethod def class_method(cls): print("是類{}的類方法,可以被實例對象、類對象調用".format(cls)) cls.__static_method_test() cls.class_method_count += 1 # 靜態方法 @staticmethod def static_method(): print("是類{}的靜態方法,可以被實例對象、類對象調用".format(Foo)) print("+++以下內容為類方法class_method()的運行結果:") Foo.class_method() @staticmethod def __static_method_test(): print("調用了靜態方法 static_method_test()") print("--"*20 + "實例方法測試" + "--"*20) obj1 = Foo("dog") obj1.instance_method() # <=> Foo.instance_method(obj1) print("--"*20 + "類方法測試" + "--"*20) obj1.class_method() print("--"*20) Foo.class_method() print("--"*20 + "靜態方法測試" + "--" * 20) obj1.static_method() print("--"*20) Foo.static_method() """ 運行結果: ----------------------------------------實例方法測試---------------------------------------- 是類<class '__main__.Foo'>的實例方法,只能被實例對象調用 產生了一個<dog>實例,共有<1>個實例對象 ----------------------------------------類方法測試---------------------------------------- 是類<class '__main__.Foo'>的類方法,可以被實例對象、類對象調用 調用了靜態方法 static_method_test() ---------------------------------------- 是類<class '__main__.Foo'>的類方法,可以被實例對象、類對象調用 調用了靜態方法 static_method_test() ----------------------------------------靜態方法測試---------------------------------------- 是類<class '__main__.Foo'>的靜態方法,可以被實例對象、類對象調用 +++以下內容為類方法class_method()的運行結果: 是類<class '__main__.Foo'>的類方法,可以被實例對象、類對象調用 調用了靜態方法 static_method_test() ---------------------------------------- 是類<class '__main__.Foo'>的靜態方法,可以被實例對象、類對象調用 +++以下內容為類方法class_method()的運行結果: 是類<class '__main__.Foo'>的類方法,可以被實例對象、類對象調用 調用了靜態方法 static_method_test() """
從案例1中得到,類方法與靜態方法可以相互調用,但是靜態方法只能用【類名】表示,而類方法用【cls】就比較方便了。
案例2:實例方法、類方法、靜態方法在繼承中(子類重寫父類中的方法)的使用
class Foo(object): X = 1 Y = 14 @staticmethod def average(*mixes): print("父類中的靜態方法 average(*mixes)") print("*****", mixes) return sum(mixes) / len(mixes) @staticmethod def static_method(): print("父類中的靜態方法 static_method()") return Foo.average(Foo.X, Foo.Y) # 注:因為這兒已經限定了只允許調用父類中的average() @classmethod def class_method(cls): # 父類中的類方法 print("父類中的類方法 class_method(cls)") return cls.average(cls.X, cls.Y) # 注:若用子類對象調用該函數,此時的cls==Son,故調用子類重寫后的average() class Son(Foo): @staticmethod def average(*mixes): # "子類中重載了父類的靜態方法" print("子類中重載了父類的靜態方法 average(*mixes)") print("*****", mixes) return sum(mixes) / len(mixes) print(Son.average(1, 2, 3), "\n" + "---" * 20) print(Son.class_method(), "\n" + "---" * 20) print(Son.static_method(), "\n" + "---" * 20) """ 運行結果: ------------------------------------------------------------ 子類中重載了父類的靜態方法 average(*mixes) ***** (1, 2, 3) 2.0 ------------------------------------------------------------ 父類中的類方法 class_method(cls) 子類中重載了父類的靜態方法 average(*mixes) ***** (1, 14) 7.5 ------------------------------------------------------------ 父類中的靜態方法 static_method() 父類中的靜態方法 average(*mixes) ***** (1, 14) 7.5 ------------------------------------------------------------ """
從案例2中得到,子類對象調用父類中的 類方法 class_method(cls) 時,由於【cls】此時為【Son】,故會執行子類中的類方法、靜態方法、靜態屬性;進一步推斷出,類方法中【cls】取決於類方法的調用者,只有發生類方法調用后才能知道執行結果。而父類中靜態方法 static_method()只能調用父類中類方法、靜態方法、靜態屬性,與靜態方法的調用者無關;進一步推斷,靜態方法可以提前預知程序的執行結果,如執行當前類中的某個類方法或靜態方法。
對案例2做一個多態測試,添加代碼如下:
def test_polymorphic(foo): """ 多態測試 :param foo: 父類對象 :return: None """ print(f.average(1, 2, 3)) f = Foo() test_polymorphic(f) print("----"*10) f = Son() # 會觸發多態 test_polymorphic(f) """ 父類中的靜態方法 average(*mixes) ***** (1, 2, 3) 2.0 ---------------------------------------- 子類中重載了父類的靜態方法 average(*mixes) ***** (1, 2, 3) 2.0 """
繼續做多態測試,添加代碼如下:
def test_polymorphic(foo): """ 多態測試 :param foo: 父類對象 :return: None """ print(f.class_method()) f = Foo() test_polymorphic(f) print("----"*10) f = Son() # 會觸發多態 test_polymorphic(f) """ 父類中的類方法 class_method(cls) 父類中的靜態方法 average(*mixes) ***** (1, 14) 7.5 ---------------------------------------- 父類中的類方法 class_method(cls) 子類中重載了父類的靜態方法 average(*mixes) ***** (1, 14) 7.5 """
案例3:使用類方法或靜態方法初始化類(可以自定義類的初始方式)
class Book(object): def __init__(self, title): self.__title = title @classmethod def object_create_by_class_method(cls, title_list): """ 使用生成器實例化多個對象 :param title_list: 每個對象的初始化參數,List :return:迭代器,每個實例化對象 """ for title in title_list: yield cls(title=title) @staticmethod def object_create_by_static_method(title_list): # 功能與類方法一樣 for title in title_list: yield Book(title=title) @property def title(self): return self.__title @title.setter def title(self, value): if not isinstance(value, str): raise TypeError('%s must be str' % value) self.__title = value @title.deleter def title(self): del self.__title # raise TypeError('Can not delete') books = ["Chinese", "mathematics", "English"] g_books = Book.object_create_by_class_method(books) print(g_books) # <generator object Book.object_create_by_class_method at 0x000001FB72AFEEC8> print(g_books.__next__().title) # 查看書的title -- Chinese print(g_books.__next__().title) # 查看書的title -- mathematics book = g_books.__next__() # 得到一個實例化對象 print(book.title) # 查看書的title -- English print(book.__dict__) # {'_Book__title': 'English'} book.title = "英語" # 修改屬性 print(book.title) # 查看書的title -- 英語 print(book.__dict__) # {'_Book__title': '英語'} del book.title # 刪除屬性 print(book.__dict__) # {}
從案例3中得到,使用類方法或者靜態方法可以自定義類的初始化方式,本案例中實現的功能是使用生成器批量創建多個對象。同時,案列中使用了【property】屬性,property的作用相當於執行了某個函數,並獲得該函數的返回值;其使用方式有3種,分別為【@property --- get】、【@函數名.setter --- set】、【@函數名.deleter --- del】,后二種必須在第一種使用的情況下才能使用,在表現形式上就是通過實例化對象間接訪問屬性。
最后,我們對案例3做微小改變,來看看實例方法、類方法、靜態方法與類、對象之間的關系,代碼如下:
class Book(object): @classmethod def object_create_by_class_method(cls): pass @staticmethod def object_create_by_static_method(): pass def instance_method(self): pass book = Book() print(book.instance_method) # 方法(綁定到對象:發生調用時,將【實例對象=self】自動作為第一個參數傳入實例方法中) print(book.object_create_by_class_method) # 方法(綁定到類:發生調用時,將【類=cls】自動作為第一個參數傳入類方法中) print(book.object_create_by_static_method) # 普通函數(非綁定:發生調用時,不需要傳入任何參數,如self,cls) print(Book.instance_method) # 普通函數(非綁定,因為實例方法就存儲在類的命名空間中) print(Book.object_create_by_class_method) # 綁定到類 print(Book.object_create_by_static_method) # 普通函數(非綁定) """ <bound method Book.instance_method of <__main__.Book object at 0x000002481CEDE988>> <bound method Book.object_create_by_class_method of <class '__main__.Book'>> <function Book.object_create_by_static_method at 0x000002481D12C828> <function Book.instance_method at 0x000002481D12C8B8> <bound method Book.object_create_by_class_method of <class '__main__.Book'>> <function Book.object_create_by_static_method at 0x000002481D12C828> """
可以發現,
1)類中的實例方法都綁定了實例對象,故建議使用實例對象調用實例方法;
2)類中的類方法都綁定了類,故建議使用類對象調用類方法,即使使用實例對象調用類方法,仍然自動將【類名】作為第一個參數傳入類方法。
3)而對於靜態方法而言,無論使用實例對象或者類對象調用其方法都不會自動進行參數傳遞,故不做限制。
4)若能用類解決的問題,就將類中的方法定義為類方法,這樣可以避免實例化對象的開銷。
綁定與非綁定的參考鏈接: