背景
剛開(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ò)程
理解了self和cls是什么時(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è)object是Python所有的新式類(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í)例。