標籤:
方法
在上一篇隨筆中,簡單提到了類的某些方法:__init__()等的調用,並簡要說明方法和函數的區別。
方法是在類內部定義的函數,方法也是對象,所以方法是類的屬性,這就是為什麼說執行個體的方法存在於類定義中。而在ruby中,方法肯定是存在於類中的,執行個體的單件方法就存在於單件類中,python中並沒有單件類,並且方法本質也是屬性,所以執行個體的方法也可以存在於自身,而在調用方法時,同樣遵循命名空間的尋找順序。但是方法和一般是屬性任然存在區別:
from types import MethodType
class MyClass:
? ? #@staticmethod
? ? def my_method(self):
? ? ? ? print("This is a class method")
obj = MyClass()
print(‘obj_m‘,id(obj.my_method))
#obj2 = MyClass()
#print(‘obj2_m‘,id(obj2.my_method))
#a = obj.my_method
print(‘cls_m‘,id(MyClass.my_method))
def my_method(self):
? ? print("This is a obj method")
obj.my_method = MethodType(my_method,obj)
print(‘obj_m‘,id(obj.my_method))
print(‘cls_m‘,id(MyClass.my_method))
def my_method(self):
? ? print("This is a obj method2")
obj.my_method = MethodType(my_method,obj)
print(‘obj_m‘,id(obj.my_method))
得到的結果為:
obj_m 4337531000
cls_m 4337792688
obj_m 4337531000
cls_m 4337792688
obj_m 4337531792
可以看到,一開始設定了一個普通函數,結果發現在類中該函數的引用和對象中函數的引用並不相同,而在對象中對該函數進行修改時,第一次修改並沒有改變引用,第二次修改時,引用卻發生改變了,而類對函數的引用當然是不會變的。由於第二次修改確實是改變了引用,說明函數確實是不可更改類型(這一點可以通過更加簡單的實驗得到),那第一次對對象改變函數為什麼並沒有改變其引用呢?其實是因為在這裡對象的函數和類的函數並不是同一個引用,當第一次改變對象的函數時,原來的對象的函數引用次數就變為0,地址被系統回收,當我們給對象定義一個同名函數時,又分到了同樣的地址,實際上是改變了引用了。可以通過增加該方法的引用次數來驗證(第四處注釋,如果去掉注釋,引用將會不同)。
好了,繞了很多的彎路又回到原點:對象的函數和類的函數一開始就不是同一個引用,這是因為,在對象調用函數(這裡強調函數是因為就算去掉self也是一樣)時,會給函數傳遞該對象的綁定,所以對象調用的函數是有綁定的函數,而類調用的是沒有綁定的函數,所以他們的引用不一樣。方法的引用僅僅是因為調用了一個綁定而變得不同,綁定不同的對象對函數的引用沒有影響(去掉第二、三行的注釋可以實驗)。
為了驗證,可以實驗以下代碼:
class MyClass:
? ? pass
class MyClass2:
? ? pass
print(id(MyClass2.__new__))
print(id(MyClass.__new__))
print(id(type.__new__))
靜態方法和類方法
在上述代碼的第一行注釋中,有@staticmethod裝飾器,如果去掉該注釋,則會發現類和對象對函數的引用是一樣的,這就是靜態方法,靜態方法對類和對象是平等的,但是並不表示完全相同,因為類對函數的調用仍然不會附帶綁定:如果在靜態方法中調用self,那麼對於對象而言,仍然不需要傳入自身,而對於類而言,必須傳入一個對象以供調用(可以傳入MyClass),因為此時只是調用了MyClass命名空間中的一個函數,並沒有獲得MyClass的綁定。
之所以對象調用方法時,不用顯式傳入自身,是因為方法在類中定義時,self的含義就是類執行個體化後的對象,對象在調用自己類的方法時,解譯器會協助將該對象傳給方法。
類方法則指的是第一個參數為類本身,沒有像self那樣的名字,但是一般使用cls作為變數名:
class MyClass:
? ? @classmethod
? ? def a_method(cls):
? ? ? ? print(cls)
MyClass.a_method()
此時,也可以不用顯式地傳入對象。
類定製
python中所謂的類定製,就是為類實現某一些特定的方法,使其具有一些特定的屬性,或者可以對其進行一些特定操作。這些特殊的方法以雙底線開頭並且以雙底線結尾。其實__init__方法就是最基本的定製類的特殊方法,通過__init__方法可以定製類執行個體的執行個體化過程。
其中一些可能比較常用的定製有:
1. 定製__str__方法,可以使類的執行個體可以通過str()轉換為字串,並且由於print列印時其實是調用了str(),所以可以自訂列印的格式;
2. 定製__len__方法,可以自訂調用len()時,返回的值;
3. 定製__iter__方法,可以使類的執行個體成為一個迭代對象,此時,需要實現__next__方法(python2中為next方法),使迭代過程完整執行,在__next__方法中,指定StopIteration錯誤,以終止迭代。
4. 定製__cmp__方法,__lt__、__le__、__gt__、__ge__、__eq__、__ne__等方法可以自訂比較大小的規則;
5. 定製__*add__、__*sub__等等數值操作的方法,可以自訂類執行個體的+-*/等操作,*表示存在__add__、__radd__、__iadd__等多種方法,預設方法從左往右計算,r開頭的方法從右往左計算,i開頭的表示左結合操作符和賦值的組合,類似於+=
6. 定製__call__方法可以使類的執行個體能夠被調用
等等
如果想得到更加具體的例子,以下文章應該會由很大協助:
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013946328809098c1be08a2c7e4319bd60269f62be04fa000
Python學習_12_方法和類定製