Python3實(shí)現(xiàn)單例模式

文末有一些補(bǔ)充內(nèi)容岛琼,如果你不太理解本文中提到的這些函數(shù)的意義或者不知道元類是什么東西凤粗,可以先查看補(bǔ)充內(nèi)容,以幫助你做好理解本文所必要的知識(shí)儲(chǔ)備月趟。

1. 在進(jìn)入正題之前,我們需要先了解兩個(gè)有特殊用途的函數(shù)恢口,
__new__()__call__().

__new__()
它是在創(chuàng)建實(shí)例的時(shí)候被調(diào)用(注意此處的"實(shí)例"孝宗,我在這里并沒有說(shuō)"的實(shí)例",因?yàn)槌祟惛纾€有元類因妇,元類創(chuàng)建實(shí)例的時(shí)候也會(huì)調(diào)用這個(gè)函數(shù))

__call__()
官方定義:Called when the instance is "called" as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
它是在“實(shí)例被當(dāng)成函數(shù)調(diào)用時(shí)”被調(diào)用。
舉個(gè)例子猿诸,實(shí)例如果是"Demo"婚被,那么,當(dāng)你寫下"Demo()"的時(shí)候梳虽,該實(shí)例(即Demo)的創(chuàng)建者(注意:此處提到的創(chuàng)建者既有可能是類址芯,也有可能是元類)中的__call__()被調(diào)用。

如果這個(gè)實(shí)例是一個(gè)類窜觉,那么它的創(chuàng)建者就是一個(gè)元類是复,如果這個(gè)實(shí)例是一個(gè)對(duì)象,那么它的創(chuàng)建者就是一個(gè)類竖螃。

所以淑廊,要利用Python實(shí)現(xiàn)單例模式,我們也有兩種思路:

2. (下面是正題特咆,我們來(lái)談一談如何在Python3中實(shí)現(xiàn)單例模式):

第一種思路是利用元類季惩,元類的實(shí)例是類录粱,而類被當(dāng)成函數(shù)調(diào)用時(shí)不就是對(duì)象嗎?(假設(shè)類名是Demo画拾,則Demo()就創(chuàng)建并返回了一個(gè)對(duì)象)因此啥繁,我們可以通過(guò)定制元類中的__call__()來(lái)實(shí)現(xiàn)單例。
代碼如下:

class Singleton(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=Singleton):
    pass

這里青抛,Foo是元類"Singleton"的實(shí)例旗闽,Foo()就是元類"Singleton"的實(shí)例被當(dāng)成函數(shù)在調(diào)用,因此元類"Singleton"中的__call__()就會(huì)被調(diào)用蜜另。

我們輸入以下代碼來(lái)測(cè)試一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

第二種思路則不需要用到元類适室。
由于__new__()是在創(chuàng)建實(shí)例的時(shí)候被調(diào)用(即,如果你創(chuàng)建一個(gè)類Foo举瑰,并且這個(gè)Foo中含有一個(gè)__new__()的方法捣辆,那么該方法將在你創(chuàng)建這個(gè)Foo類的對(duì)象時(shí)被調(diào)用,即在你寫Foo()的時(shí)候被調(diào)用)此迅,所以我們可以通過(guò)直接定制__new__()來(lái)實(shí)現(xiàn)單例汽畴。
代碼如下:

class Foo(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

測(cè)試一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

當(dāng)然,如果你不想在當(dāng)前這個(gè)類中定制__new__()方法耸序,你也可以在當(dāng)前類的父類中定制__new__()忍些,python的解釋器依然能夠找到并正確執(zhí)行這個(gè)函數(shù),畢竟子類已經(jīng)繼承了父類的方法了坎怪。如下:

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

class Foo(Singleton):
    pass

再測(cè)試一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

以上就是本篇教程的全部核心內(nèi)容罢坝,謝謝您的閱讀!

補(bǔ)充內(nèi)容:

  1. 什么是元類芋忿?
    簡(jiǎn)單回答:對(duì)象的抽象化是類炸客,而類的抽象化就是元類疾棵,或者說(shuō)戈钢,對(duì)象是類的實(shí)例,類就是元類的實(shí)例是尔,還可以說(shuō)殉了,類具體化后得到對(duì)象,元類具體化后得到類拟枚。
    具體回答:詳見本博客的后續(xù)博文薪铜。

  2. 諸如以上提到的__new__()__call__()這類函數(shù),它們存在的意義是什么恩溅?我們到底為什么需要它們隔箍?
    :以上這樣的函數(shù)還有很多,它們最主要的用途就是幫助我們定制化我們自己的類脚乡,讓類的功能更符合我們的需求蜒滩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子俯艰,更是在濱河造成了極大的恐慌捡遍,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竹握,死亡現(xiàn)場(chǎng)離奇詭異画株,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)啦辐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門谓传,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人昧甘,你說(shuō)我怎么就攤上這事良拼。” “怎么了充边?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵庸推,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我浇冰,道長(zhǎng)贬媒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任肘习,我火速辦了婚禮际乘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘漂佩。我一直安慰自己脖含,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布投蝉。 她就那樣靜靜地躺著养葵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘩缆。 梳的紋絲不亂的頭發(fā)上关拒,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音庸娱,去河邊找鬼着绊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛熟尉,可吹牛的內(nèi)容都是我干的归露。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼斤儿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剧包!你這毒婦竟也來(lái)了腮考?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤玄捕,失蹤者是張志新(化名)和其女友劉穎踩蔚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枚粘,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馅闽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馍迄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片福也。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖攀圈,靈堂內(nèi)的尸體忽然破棺而出暴凑,到底是詐尸還是另有隱情,我是刑警寧澤赘来,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布现喳,位于F島的核電站,受9級(jí)特大地震影響犬辰,放射性物質(zhì)發(fā)生泄漏嗦篱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一幌缝、第九天 我趴在偏房一處隱蔽的房頂上張望灸促。 院中可真熱鬧,春花似錦涵卵、人聲如沸浴栽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)典鸡。三九已至,卻和暖如春贴硫,著一層夾襖步出監(jiān)牢的瞬間椿每,已是汗流浹背伊者。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工英遭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亦渗。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓挖诸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親法精。 傳聞我的和親對(duì)象是個(gè)殘疾皇子多律,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理痴突,服務(wù)發(fā)現(xiàn),斷路器狼荞,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法辽装,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法相味,繼承相關(guān)的語(yǔ)法拾积,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語(yǔ)閱讀 3,665評(píng)論 0 7
  • 問(wèn)題1 表述以及解決方案 一個(gè)項(xiàng)目包含很多模塊所有的模塊依賴總pom文件丰涉。 這個(gè)項(xiàng)目屬于spring-boot項(xiàng)目...
    eric_fun閱讀 272評(píng)論 0 0
  • 最近總是和母親吵架拓巧,可是我是真的愛她,她受的苦不知道為什么沒有抵消我心中的怨一死,始終我不愛我自己肛度,也不夠...
    席媛閱讀 212評(píng)論 0 0