在 Python 中,執行個體方法(instance method),類方法(class method)與靜态方法(static method)經常容易混淆。本文通過代碼例子來說明它們的差別。
執行個體方法
Python 的執行個體方法用得最多,也最常見。我們先來看 Python 的執行個體方法。
class Kls(object):
def __init__(self, data):
self.data = data
def printd(self):
print(self.data)
ik1 = Kls('leo')
ik2 = Kls('lee')
ik1.printd()
ik2.printd()
輸出:
leo
lee
上述例子中,
printd
為一個執行個體方法。執行個體方法第一個參數為
self
,當使用
ik1.printd()
調用執行個體方法時,執行個體
ik1
會傳遞給
self
參數,這樣
self
參數就可以引用目前正在調用執行個體方法的執行個體。利用執行個體方法的這個特性,上述代碼正确輸出了兩個執行個體的成員資料。
類方法
Python 的類方法采用裝飾器
@classmethod
來定義,我們直接看例子。
class Kls(object):
num_inst =
def __init__(self):
Kls.num_inst = Kls.num_inst +
@classmethod
def get_no_of_instance(cls):
return cls.num_inst
ik1 = Kls()
ik2 = Kls()
print ik1.get_no_of_instance()
print Kls.get_no_of_instance()
輸出:
2
2
在上述例子中,我們需要統計類
Kls
執行個體的個數,是以定義了一個類變量
num_inst
來存放執行個體個數。通過裝飾器
@classmethod
的使用,方法
get_no_of_instance
被定義成一個類方法。在調用類方法時,Python 會将類(class Kls)傳遞給
cls
,這樣在
get_no_of_instance
内部就可以引用類變量
num_inst
。
由于在調用類方法時,隻需要将類型本身傳遞給類方法,是以,既可以通過類也可以通過執行個體來調用類方法。
靜态方法
在開發中,我們常常需要定義一些方法,這些方法跟類有關,但在實作時并不需要引用類或者執行個體,例如,設定環境變量,修改另一個類的變量,等。這個時候,我們可以使用靜态方法。
Python 使用裝飾器
@staticmethod
來定義一個靜态方法。
IND = 'ON'
class Kls(object):
def __init__(self, data):
self.data = data
@staticmethod
def checkind():
return IND == 'ON'
def do_reset(self):
if self.checkind():
print('Reset done for: %s' % self.data)
def set_db(self):
if self.checkind():
print('DB connection made for: %s' % self.data)
ik1 = Kls()
ik1.do_reset()
ik1.set_db()
輸出:
Reset done for: 24
DB connection made for: 24
在代碼中,我們定義了一個全局變量
IND
,由于
IND
跟類
Kls
相關,是以我們将方法
checkind
放置在類
Kls
中定義。方法
checkind
隻需檢查
IND
的值,而不需要引用類或者執行個體,是以,我們将方法
checkind
定義為靜态方法。
對于靜态方法,Python 并不需要傳遞類或者執行個體,是以,既可以使用類也可以使用執行個體來調用靜态方法。
執行個體方法,類方法與靜态方法的差別
我們用代碼說明執行個體方法,類方法,靜态方法的差別。注意下述代碼中方法
foo
,
class_foo
,
static_foo
的定義以及使用。
class Kls(object):
def foo(self, x):
print('executing foo(%s,%s)' % (self, x))
@classmethod
def class_foo(cls,x):
print('executing class_foo(%s,%s)' % (cls,x))
@staticmethod
def static_foo(x):
print('executing static_foo(%s)' % x)
ik = Kls()
# 執行個體方法
ik.foo()
print(ik.foo)
print('==========================================')
# 類方法
ik.class_foo()
Kls.class_foo()
print(ik.class_foo)
print('==========================================')
# 靜态方法
ik.static_foo()
Kls.static_foo('hi')
print(ik.static_foo)
輸出:
executing foo(<__main__.Kls object at x0551E190>,)
<bound method Kls.foo of <__main__.Kls object at x0551E190>>
==========================================
executing class_foo(<class '__main__.Kls'>,)
executing class_foo(<class '__main__.Kls'>,)
<bound method type.class_foo of <class '__main__.Kls'>>
==========================================
executing static_foo()
executing static_foo(hi)
<function static_foo at x055238B0>
對于執行個體方法,調用時會把執行個體
ik
作為第一個參數傳遞給
self
參數。是以,調用
ik.foo(1)
時輸出了執行個體
ik
的位址。
對于類方法,調用時會把類
Kls
作為第一個參數傳遞給
cls
參數。是以,調用
ik.class_foo(1)
時輸出了
Kls
類型資訊。
前面提到,可以通過類也可以通過執行個體來調用類方法,在上述代碼中,我們再一次進行了驗證。
對于靜态方法,調用時并不需要傳遞類或者執行個體。其實,靜态方法很像我們在類外定義的函數,隻不過靜态方法可以通過類或者執行個體來調用而已。
值得注意的是,在上述例子中,
foo
隻是個函數,但當調用
ik.foo
的時候我們得到的是一個已經跟執行個體
ik
綁定的函數。調用
foo
時需要兩個參數,但調用
ik.foo
時隻需要一個參數。
foo
跟
ik
進行了綁定,是以,當我們列印
ik.foo
時,會看到以下輸出:
<bound method Kls.foo of <__main__.Kls object at x0551E190>>
當調用
ik.class_foo
時,由于
class_foo
是類方法,是以,
class_foo
跟
Kls
進行了綁定(而不是跟
ik
綁定)。當我們列印
ik.class_foo
時,輸出:
<bound method type.class_foo of <class '__main__.Kls'>>
當調用
ik.static_foo
時,靜态方法并不會與類或者執行個體綁定,是以,列印
ik.static_foo
(或者
Kls.static_foo
)時輸出:
<function static_foo at x055238B0>
概括來說,是否與類或者執行個體進行綁定,這就是執行個體方法,類方法,靜态方法的差別。
參考資料
- https://www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/
- https://taizilongxu.gitbooks.io/stackoverflow-about-python/content/14/README.html
- https://kuanghy.github.io/2015/12/19/python-variable
- https://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python