24.python中類的方法


  類中的方法,其實就是類中的函數,可以分為:實例方法,類方法,靜態方法。方法和字段一樣,也是屬於類的屬性,所以也具有運行中修改的特效, 但一般不推薦這樣做。

  我在類的基本語法中,介紹了構造器方法:__init__ 、__new__;解構器方法:__del__;

  注意,這里雖然是以兩個下划線(__)開頭,但同時以兩個下划線(__)結尾,這里表明其是一個‘魔法方法’,關於類中的魔法方法,將起一篇進行說明。

  但是,如果單純只以兩個下划線開始,則依然是私有化的意思,看代碼示例:

class Test(object):

    def __scolia__(self):   # 一個類似魔術方法,並不是私有化
        return 'scolia'

    def __good(self):   # 私有方法
        return 'good'

a = Test()
print a.__scolia__()   # 魔法方法可以在直接訪問
print a.__good()    # 私有方法不能直接訪問

 

  同樣的,和字段私有化一樣,我們也可能同特殊手段進行強制訪問:

print a._Test__good()  # 強制訪問

 

  當然,私有方法也可以在類的內部訪問,和私有字段一樣。

  所以說,屬性的私有化都是對訪問入口進行混淆,同樣的,也不建議強制訪問私有屬性。

  也許這里的‘魔法方法’看起來並不‘魔法’,詳情將以后解釋。


實例方法:

  在 __init__ 構造器中,提起過其是一個實例方法,實例方法的特點就是:

  1.方法的第一個參數必須是 self,當然這是約定俗成的寫法,你可以將 self 換成 abc 之類的,但是為了別的程序員能看得懂,還是統一用 self 吧。這里的 self 代表實例本身,也就是說如果我實例化時使用的是: a = Test() ,那么 self 就代表 a 這個實例,我們可以在很多構造器中看到類似 self.scolia = 'good' 的寫法,其實這個寫法和在類外面 a.scolia = 'good' 效果一樣,是為了添加屬性,只不過 __init__ 方法是實例化時自動調用的函數,所以適合進行初始屬性的創建。

  2.實例方法在調用的時候,self 是自動傳遞的,所以不需要我們再處理。

  3.實例方法一般要有實例才能調用,當然也有特殊的調用方法。

代碼示例:

class Test(object):

    def __init__(self, a, b):   # 構造器在實例創建時進行屬性的初始化
        self.a = int(a)
        self.b = int(b)

    def abc(self, c):  # 實例方法
        print self.a + self.b + int(c)  # 因為self是自動傳遞的,所以我們可以在實例方法中調用實例的屬性

a = Test(123, 321)  # 我們只要為 a 和 b 傳參就行了
a.abc(666)  # 同樣的,只要為 c 傳參

 

  這里,將引入一個綁定 (binding) 的概念,其主要和方法的調用有關。

  首先,我們知道方法是類的屬性,而不是實例的屬性,在上篇博文類的屬性和實例的屬性中我們也討論過這個問題。

  其次,方法只有在其所屬的類擁有實例時,才能被調用。當一個類存在實例后,方法才被認為是綁定到這個實例。沒有實例的時候,方法是未綁定的。

  最后,任何一個方法定義的第一個參數都是變量 self ,它表示調用此方法的實例對象。

  很明顯這里的綁定針對的是實例方法。因為如果沒有實例的話,self 就無法傳遞,這將導致參數的不足,當然就無法調用了。

  但是,我們可以自己傳遞 self 來調用未綁定的方法。調用未綁定的方法通常是在我們繼承了一個父類后, 我們覆蓋了父類中的某個方法,但是為了實現代碼重用,我們又想在子類中調用父類的方法。單純的復制父類中的代碼明顯不是一個好選擇, 除了浪費系統資源之外,還有可能在復制的時候出錯,而且以后修改父類的代碼之后,還要修改相應子類中的代碼,實在太低效,這個時候就是調用未綁定方法的場景。

代碼示例:

class abc(object):
    def __init__(self, a):
        self.a = -int(a)

class Test(abc):
    def __init__(self, a, b):
        abc.__init__(self, a)   # 調用父類的構造器,並手動傳遞 self
        self.b = b

    def fangfa(self):
        print self.a + self.b   # 屬性 a 由父類的構造器創建,b 由子類構造器創建

a = Test(123, 321)  # 我們只創建了子類的實例,而沒有創建父類的實例
a.fangfa()

 

  本來我們沒有創建父類的示例,是無法調用父類的實例方法的,但是我們手動傳遞了實例方法需要的 self 參數,就可以實現調用了。

  這里的順序是,我們創建了 Test 的實例,其 self 是自動傳遞的,故 Test 的構造方法 __init__(self, a, b) 中 self 就代表實例 a,而我們又調用了父類的 abc.__init__(self, a) 這里的 self 就是子類的實例 a ,參數 a 就是我們傳的 123,而父類中 self.a = -int(a) ;最后我們可在子類的方法中調用 self.a 這個屬性。

 


 

類方法:

  類方法其實和實例方法類似,不過其第一個參數一般是 cls (約定俗成)而不是 self。但是,如果我們直接將 self 換成 cls 來創建類方法是不對的,因為實例方法的首個參數也是任意的,只是統一使用 self 。python的解釋器並沒有說看見第一個參數是 cls 就知道這個是類方法,它還是將其當作是實例方法來對待,所以我們需要通過內建函數: classmethod() 來創建類方法。

代碼示例:

class Test(object):

    def abc(cls):
        print cls.__name__  # 打印類名

    abc = classmethod(abc)  # 通過普通的函數傳參的方式創建類方法

a = Test()  
Test.abc()  # 類能調用
a.abc()     # 實例也能調用

 

  當然,有同學在看到這段代碼的時候想到了裝飾器,實際上我們也可以使用裝飾器的方法來創建類方法:

class Test(object):
    @classmethod
    def abc(cls):
        print cls.__name__  # 打印類名

 

  結果也是一樣的,具體選擇看個人喜好。

  注意,因為使用了  classmethod()函數,其第一個參數 cls 是固定傳遞的,而且是代表當前的類。並沒有實例方法中非綁定方法的調用形式。

 


 

靜態方法:

  靜態方法其實就是類中的一個普通函數,它並沒有默認傳遞的參數,在創建靜態方法的時候,需要用到內置函數: staticmethod()

代碼示例:

class Test(object):

    def abc():
        print 'abc'
        
    abc = staticmethod(abc)

    @staticmethod
    def xyz(a, b):
        print a + b

Test.abc()  # 類調用
Test.xyz(1, 2)  # 類調用
a = Test()
a.abc()     # 實例調用
a.xyz(3, 4)  # 實例調用

 

  注意,雖然靜態方法沒有默認參數, 但並不代表不能有參數。


 

總結:

  1.實例方法需要至少一個默認參數 self。

  2.類方法需要至少一個默認參數 cls。

  3.靜態方法不需要默認參數。

  另外:方法也是屬性,所以在類的內部,我們可通過 self.abc()、cls.abc() 的方式來調用類中的其他方法,當然要注意傳參的問題。

 


用方法冒充字段:

  有時候,我們的一個方法在經過一系列處理以后,返回的是一個數據,例如:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa(self):
        c = self.a + self.b
        return c    # 返回處理的結果數據


a = Test(1, 2)
b = a.fangfa()    # 調用方法,得到返回值
print b

 

  但是,懶惰的程序員們想:我想要的只是和字段類似的數據,卻要調用一個方法,有時候容易搞錯,能不能用字段的形式獲取數據呢?這樣更加符合直覺。

  可以,只有使用 property() 函數就可以了。同樣的,這里也有兩種創建方式,這里只演示裝飾器的方式:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c


a = Test(1, 2)
b = a.fangfa    # 不用帶括號了
print b

 

  這樣是實現了方法偽裝成字段了。其實就是懶惰的程序員們不願意多寫一個括號,當然還有一些其他好處。

  另外,函數要用返回值,不然就默認為 None 了。

  如果在經典類中,我們就只能做到這樣了。

  但是,使用新式類的話,就能有更多的功能:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c

    @fangfa.setter
    def fangfa(self, value):
        self.a = value

    @fangfa.deleter
    def fangfa(self):
        print '屬性已刪除'

a = Test(1, 2)
b = a.fangfa   # 獲得方法的返回值
print b
a.fangfa = 100    # 執行 fangfa.setter 修飾的方法,並讓value = 100
print a.a
del a.fangfa    # 執行 fangfa.deleter 修飾的方法

  注意后面兩個裝飾器的名字

  另外,方法必須要先經過 property()函數的裝飾后,才有后面兩個裝飾器的用法。

  如果使用非裝飾器的形式的話:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa_get(self):
        c = self.a + self.b
        return c

    def fangfa_set(self, value):
        self.a = value

    def fangfa_del(self):
        print '屬性已刪除'

    fangfa = property(fangfa_get, fangfa_set, fangfa_del)

 

    property(fget=None, fset=None, fdel=None, doc=None)  

  最后的 doc 是說明文檔,看個人需要添加,可通過 Test.fangfa.__doc__ 進行訪問。


  關於方法就先說這么多,以后有需要再進行修改補充。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM