Python 實例方法、類方法和靜態方法的區別及使用

在 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 = 0

    def __init__(self):
        Kls.num_inst = Kls.num_inst + 1

    @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

在上述例子中,我們需要統計類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(24)
ik1.do_reset()
ik1.set_db()

輸出:

Reset done for: 24 
DB connection made for: 24

在代碼中,我們定義了一個全局變量IND,由於IND跟類Kls相關,所以我們將方法checkind放置在類Kls中定義。方法checkind只需檢查IND的值,而不需要引用類或者實例,因此,我們將方法checkind定義爲靜態方法。 
對於靜態方法,Python 並不需要傳遞類或者實例,因此,既可以使用類也可以使用實例來調用靜態方法。

實例方法,類方法與靜態方法的區別

我們用代碼說明實例方法,類方法,靜態方法的區別。注意下述代碼中方法fooclass_foostatic_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(1)
print(ik.foo)
print('==========================================')

# 類方法
ik.class_foo(1)
Kls.class_foo(1)
print(ik.class_foo)
print('==========================================')

# 靜態方法
ik.static_foo(1)
Kls.static_foo('hi')
print(ik.static_foo)

輸出:

executing foo(<__main__.Kls object at 0x0551E190>,1)
<bound method Kls.foo of <__main__.Kls object at 0x0551E190>>
==========================================
executing class_foo(<class '__main__.Kls'>,1)
executing class_foo(<class '__main__.Kls'>,1)
<bound method type.class_foo of <class '__main__.Kls'>>
==========================================
executing static_foo(1)
executing static_foo(hi)
<function static_foo at 0x055238B0>

對於實例方法,調用時會把實例ik作爲第一個參數傳遞給self參數。因此,調用ik.foo(1)時輸出了實例ik的地址。

對於類方法,調用時會把類Kls作爲第一個參數傳遞給cls參數。因此,調用ik.class_foo(1)時輸出了Kls類型信息。 
前面提到,可以通過類也可以通過實例來調用類方法,在上述代碼中,我們再一次進行了驗證。

對於靜態方法,調用時並不需要傳遞類或者實例。其實,靜態方法很像我們在類外定義的函數,只不過靜態方法可以通過類或者實例來調用而已。

值得注意的是,在上述例子中,foo只是個函數,但當調用ik.foo的時候我們得到的是一個已經跟實例ik綁定的函數。調用foo時需要兩個參數,但調用ik.foo時只需要一個參數。fooik進行了綁定,因此,當我們打印ik.foo時,會看到以下輸出:

<bound method Kls.foo of <__main__.Kls object at 0x0551E190>>

當調用ik.class_foo時,由於class_foo是類方法,因此,class_fooKls進行了綁定(而不是跟ik綁定)。當我們打印ik.class_foo時,輸出:

<bound method type.class_foo of <class '__main__.Kls'>>
  • 當調用ik.static_foo時,靜態方法並不會與類或者實例綁定,因此,打印ik.static_foo(或者Kls.static_foo)時輸出:
  • 概括來說,是否與類或者實例進行綁定,這就是實例方法,類方法,靜態方法的區別。

參考資料

  1. https://www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/
  2. https://taizilongxu.gitbooks.io/stackoverflow-about-python/content/14/README.html
  3. https://kuanghy.github.io/2015/12/19/python-variable
  4. https://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章