從self、cls看Python的實(shí)例化

背景

剛開(kāi)始學(xué)習(xí)Python的時(shí)候經(jīng)常會(huì)有一個(gè)疑問(wèn),為什么每個(gè)類(lèi)方法的第一個(gè)參與一定要加一個(gè)self?經(jīng)過(guò)一定的編碼后發(fā)現(xiàn),怎么還有一些類(lèi)方法里面寫(xiě)的是cls

實(shí)例化

在使用類(lèi)方法的時(shí)候,我們通常會(huì)把一個(gè)類(lèi)做實(shí)例化之后再進(jìn)行調(diào)用,比如這樣:

class Calc(object):
    def add(self, x, y):
        print x + y


if __name__ == '__main__':
    calc = Calc()
    calc.add(1, 1)

這個(gè)self到底是什么呢?我們加兩行代碼來(lái)看。

class Calc(object):
    def add(self, x, y):
        print x + y
        print self


if __name__ == '__main__':
    calc = Calc()
    calc.add(1, 1)
    print calc
    
--->
2
<__main__.Calc object at 0x108b4e4d0>
<__main__.Calc object at 0x108b4e4d0>   

可以看的出來(lái),這個(gè)self和實(shí)例化出來(lái)的calc都是Calc這個(gè)對(duì)象,并且指向的內(nèi)存地址是同一個(gè),也就是他們兩個(gè)是同一個(gè)東西。

靜態(tài)方法

在看別人的代碼的時(shí)候,經(jīng)常會(huì)看到一個(gè)@staticmethod。這個(gè)東西是靜態(tài)方法,在類(lèi)中的方法都必須要傳self對(duì)象,但是一旦被@staticmethod裝飾器裝飾后的方法,就不需要傳入self這個(gè)參數(shù),如下:

class Calc(object):
    def add(self, x, y):
        print x + y

    @staticmethod
    def minus(x, y):
        print x - y


if __name__ == '__main__':
    calc = Calc()
    calc.add(1, 1)
    Calc.minus(3, 2)

具體來(lái)說(shuō)這個(gè)有什么用?我個(gè)人理解來(lái)說(shuō)在Python這個(gè)裝飾器只是一個(gè)基于類(lèi)設(shè)計(jì)的一個(gè)方法。你用一個(gè)def來(lái)實(shí)現(xiàn),或者就用類(lèi)方法來(lái)實(shí)現(xiàn)影響其實(shí)并不大。當(dāng)然,你實(shí)例化了,也是可以通過(guò)實(shí)例來(lái)調(diào)用靜態(tài)方法的。

類(lèi)方法

Python中還有一個(gè)方法@classmethod,使用了這個(gè)方法,傳入的第一個(gè)參數(shù)就不是self,而是cls。比如這樣:

class Calc(object):
    def add(self, x, y):
        print x + y

    @staticmethod
    def minus(x, y):
        print x - y

    @classmethod
    def multi(cls, x, y):
        print x * y


if __name__ == '__main__':
    calc = Calc()
    calc.add(1, 1)
    Calc.minus(3, 2)
    calc.multi(2, 2)
    
--->

2
1
4   

可以在這行代碼中吧這個(gè)cls打出來(lái)看看是個(gè)什么東西,最為對(duì)比,同樣也加上打印self。

<class '__main__.Calc'>
<__main__.Calc object at 0x1033ef4d0>

這樣對(duì)比出來(lái)就非常清晰了。cls指的是這個(gè)類(lèi),如果嚴(yán)謹(jǐn)一點(diǎn)可以再加上print Calc。而self是這個(gè)類(lèi)的一個(gè)實(shí)例,是放在內(nèi)存中的。

那么這個(gè)到底有什么用呢?說(shuō)實(shí)話,一般來(lái)說(shuō)沒(méi)什么卵用,跟@staticmethod一樣。可以在不需要實(shí)例化的時(shí)候調(diào)用這個(gè)方法。

劃重點(diǎn)

如果你需要經(jīng)常對(duì)函數(shù)的結(jié)構(gòu)進(jìn)行修改,那么這個(gè)方法就非常有用了。

英文好的可以看這里Meaning of @classmethod and @staticmethod for beginner?

英文不好的就看這里Python 中的 classmethod 和 staticmethod 有什么具體用途? - 水中柳影的回答 - 知乎

實(shí)例化的過(guò)程

理解了selfcls是什么時(shí)候,可以繼續(xù)再研究實(shí)例化的過(guò)程。

Python在實(shí)例化的過(guò)程中,會(huì)首先調(diào)用__new__這個(gè)內(nèi)置的方法。如果我們重寫(xiě)這個(gè)方法,但是不按照原有的方式去寫(xiě),那么就會(huì)實(shí)例化失敗,比如這樣:

class Calc(object):
    def __init__(self):
        print "class init..."

    def __new__(cls, *args, **kwargs):
        print "new a class..."

    def add(self, x, y):
        print x + y


if __name__ == '__main__':
    calc = Calc()
    print calc
    
--->
new a class...
None

可以看到,實(shí)例化了一個(gè)None出來(lái)。
__new__方法總增加return object.__new__(cls)即可正常實(shí)例化。同時(shí)可以看到__init__方法也被執(zhí)行了。

特別說(shuō)明一下,這個(gè)objectPython所有的新式類(lèi)的基類(lèi)。

單例

了解了這些內(nèi)容,重新來(lái)看看單例模式。之前介紹了一個(gè)不嚴(yán)謹(jǐn)?shù)膯卫?,這里來(lái)看一個(gè)比較嚴(yán)謹(jǐn)?shù)膯卫纠?/p>

class Single(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_single'):
            cls._single = object.__new__(cls, *args, **kwargs)
        return cls._single

這里可以看到在__new__方法中加了一個(gè)判斷,如果類(lèi)實(shí)例化的時(shí)候沒(méi)有_single這個(gè)屬性,說(shuō)明類(lèi)還沒(méi)有被實(shí)例化,這個(gè)時(shí)候就按照正常的方法實(shí)例化,如果發(fā)現(xiàn)有_single這個(gè)屬性了,那么就直接返回類(lèi)的_single對(duì)象,也就是已經(jīng)被實(shí)例化的對(duì)象,通過(guò)這個(gè)邏輯來(lái)保證實(shí)例化的時(shí)候,只會(huì)存在一個(gè)實(shí)例。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容