摘要:本篇文章總結(jié)了目前主流的實(shí)現(xiàn)單例模式的方法供讀者參考。使用實(shí)現(xiàn)單例模式同樣,我們?cè)陬惖膭?chuàng)建時(shí)進(jìn)行干預(yù),從而達(dá)到實(shí)現(xiàn)單例的目的。
很多初學(xué)者喜歡用 全局變量 ,因?yàn)檫@比函數(shù)的參數(shù)傳來傳去更容易讓人理解。確實(shí)在很多場(chǎng)景下用全局變量很方便。不過如果代碼規(guī)模增大,并且有多個(gè)文件的時(shí)候,全局變量就會(huì)變得比較混亂。你可能不知道在哪個(gè)文件中定義了相同類型甚至重名的全局變量,也不知道這個(gè)變量在程序的某個(gè)地方被做了怎樣的操作。
因此對(duì)于這種情況,有種更好的實(shí)現(xiàn)方式:
單例(Singleton)
單例是一種 設(shè)計(jì)模式 ,應(yīng)用該模式的類只會(huì)生成一個(gè)實(shí)例。
單例模式保證了在程序的不同位置都 可以且僅可以取到同一個(gè)對(duì)象實(shí)例 :如果實(shí)例不存在,會(huì)創(chuàng)建一個(gè)實(shí)例;如果已存在就會(huì)返回這個(gè)實(shí)例。因?yàn)閱卫且粋€(gè)類,所以你也可以為其提供相應(yīng)的操作方法,以便于對(duì)這個(gè)實(shí)例進(jìn)行管理。
舉個(gè)例子來說,比如你開發(fā)一款游戲軟件,游戲中需要有“場(chǎng)景管理器”這樣一種東西,用來管理游戲場(chǎng)景的切換、資源載入、網(wǎng)絡(luò)連接等等任務(wù)。這個(gè)管理器需要有多種方法和屬性,在代碼中很多地方會(huì)被調(diào)用,且被調(diào)用的必須是同一個(gè)管理器,否則既容易產(chǎn)生沖突,也會(huì)浪費(fèi)資源。這種情況下,單例模式就是一個(gè)很好的實(shí)現(xiàn)方法。
單例模式廣泛應(yīng)用于各種開發(fā)場(chǎng)景,對(duì)于開發(fā)者而言是必須掌握的知識(shí)點(diǎn),同時(shí)在很多面試中,也是常見問題。本篇文章總結(jié)了目前主流的實(shí)現(xiàn)單例模式的方法供讀者參考。
希望看過此文的同學(xué),在以后被面到此問題時(shí),能直接皮一下面試官,“我會(huì) 4 種單例模式實(shí)現(xiàn),你想聽哪一種?”
以下是實(shí)現(xiàn)方法索引:
使用函數(shù)裝飾器實(shí)現(xiàn)單例
使用類裝飾器實(shí)現(xiàn)單例
使用 new 關(guān)鍵字實(shí)現(xiàn)單例
使用 metaclass 實(shí)現(xiàn)單例
使用函數(shù)裝飾器實(shí)現(xiàn)單例以下是實(shí)現(xiàn)代碼:
def singleton(cls): _instance = {} def inner(): if cls not in _instance: _instance[cls] = cls() return _instance[cls] return inner @singleton class Cls(object): def __init__(self): pass cls1 = Cls() cls2 = Cls() print(id(cls1) == id(cls2))
輸出結(jié)果:
True
在 Python 中,id 關(guān)鍵字可用來查看對(duì)象在內(nèi)存中的存放位置,這里 cls1 和 cls2 的 id 值相同,說明他們指向了同一個(gè)對(duì)象。
關(guān)于裝飾器的知識(shí),有不明白的同學(xué)可以查看之前的文章 【編程課堂】裝飾器淺析 或者使用搜索引擎再學(xué)習(xí)一遍。代碼中比較巧妙的一點(diǎn)是:
_instance = {}
使用不可變的 類地址 作為鍵,其實(shí)例作為值,每次創(chuàng)造實(shí)例時(shí),首先查看該類是否存在實(shí)例,存在的話直接返回該實(shí)例即可,否則新建一個(gè)實(shí)例并存放在字典中。
使用類裝飾器實(shí)現(xiàn)單例代碼:
class Singleton(object): def __init__(self, cls): self._cls = cls self._instance = {} def __call__(self): if self._cls not in self._instance: self._instance[self._cls] = self._cls() return self._instance[self._cls] @Singleton class Cls2(object): def __init__(self): pass cls1 = Cls2() cls2 = Cls2() print(id(cls1) == id(cls2))
同時(shí),由于是面對(duì)對(duì)象的,這里還可以這么用
class Cls3(): pass Cls3 = Singleton(Cls3) cls3 = Cls3() cls4 = Cls3() print(id(cls3) == id(cls4))
使用 類裝飾器實(shí)現(xiàn)單例的原理和 函數(shù)裝飾器 實(shí)現(xiàn)的原理相似,理解了上文,再理解這里應(yīng)該不難。
New、Metaclass 關(guān)鍵字在接著說另外兩種方法之前,需要了解在 Python 中一個(gè)類和一個(gè)實(shí)例是通過哪些方法以怎樣的順序被創(chuàng)造的。
簡(jiǎn)單來說, 元類 ( metaclass ) 可以通過方法 metaclass 創(chuàng)造了 類(class) ,而 類(class) 通過方法 new 創(chuàng)造了 實(shí)例(instance) 。
在單例模式應(yīng)用中,在創(chuàng)造類的過程中或者創(chuàng)造實(shí)例的過程中稍加控制達(dá)到最后產(chǎn)生的實(shí)例都是一個(gè)對(duì)象的目的。
本文主講單例模式,所以對(duì)這個(gè) topic 只會(huì)點(diǎn)到為止,有感興趣的同學(xué)可以在網(wǎng)上搜索相關(guān)內(nèi)容,幾篇參考文章:
What are metaclasses in Python?
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
python-__new__-magic-method-explained
http://howto.lintel.in/python-__new__-magic-method-explained/
Why is __init__() always called after __new__()?
https://stackoverflow.com/questions/674304/why-is-init-always-called-after-new
使用 new 關(guān)鍵字實(shí)現(xiàn)單例模式使用 new 方法在創(chuàng)造實(shí)例時(shí)進(jìn)行干預(yù),達(dá)到實(shí)現(xiàn)單例模式的目的。
class Single(object): _instance = None def __new__(cls, *args, **kw): if cls._instance is None: cls._instance = object.__new__(cls, *args, **kw) return cls._instance def __init__(self): pass single1 = Single() single2 = Single() print(id(single1) == id(single2))
在理解到 new 的應(yīng)用后,理解單例就不難了,這里使用了
_instance = None
來存放實(shí)例,如果 _instance 為 None,則新建實(shí)例,否則直接返回 _instance 存放的實(shí)例。
使用 metaclass 實(shí)現(xiàn)單例模式同樣,我們?cè)陬惖膭?chuàng)建時(shí)進(jìn)行干預(yù),從而達(dá)到實(shí)現(xiàn)單例的目的。
在實(shí)現(xiàn)單例之前,需要了解使用 type 創(chuàng)造類的方法,代碼如下:
def func(self): print("do sth") Klass = type("Klass", (), {"func": func}) c = Klass() c.func()
以上,我們使用 type 創(chuàng)造了一個(gè)類出來。這里的知識(shí)是 mataclass 實(shí)現(xiàn)單例的基礎(chǔ)。
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Cls4(metaclass=Singleton): pass cls1 = Cls4() cls2 = Cls4() print(id(cls1) == id(cls2))
這里,我們將 metaclass 指向 Singleton 類,讓 Singleton 中的 type 來創(chuàng)造新的 Cls4 實(shí)例。
小結(jié)本文雖然是講單例模式,但在實(shí)現(xiàn)單例模式的過程中,涉及到了蠻多高級(jí) Python 語法,包括裝飾器、元類、new、type 甚至 super 等等。對(duì)于新手同學(xué)可能難以理解,其實(shí)在工程項(xiàng)目中并不需要你掌握的面面俱到,掌握其中一種,剩下的作為了解即可。
by 周鑫鑫
關(guān)于更多的設(shè)計(jì)模式,給初學(xué)者推薦《 Head First 設(shè)計(jì)模式 》(Head First Design Patterns),此書淺顯易懂,在 Head First 系列書籍里面也算是很好的一本。
我們的資源網(wǎng)盤里有電子版,獲取地址請(qǐng)?jiān)诠娞?hào)( Crossin的編程教室 )里回復(fù)關(guān)鍵字: 資源
════
其他文章及回答:
如何自學(xué)Python | 新手引導(dǎo) | 精選Python問答 | Python單詞表 | 區(qū)塊鏈 | 人工智能 | 雙11 | 嘻哈 | 爬蟲 | 排序算法
歡迎搜索及關(guān)注: Crossin的編程教室
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/42490.html
摘要:中的類都是單例模式一天,一同事問我這樣一個(gè)問題。與方法屬于新式類,即屬于類。方法在實(shí)例被創(chuàng)建之后被調(diào)用,該方法僅僅是對(duì)方法創(chuàng)建的實(shí)例進(jìn)行一些初始化操作。需要注意的是,在重寫方法與方法的參數(shù)應(yīng)該保持一致,否則會(huì)有發(fā)生。 Python 中的類都是單例模式? 一天,一同事問我這樣一個(gè)問題。這是一個(gè)奇怪的問題,可能你也這么認(rèn)為。這里先不做解釋,我們先來看看 __new__ 和 __init__...
摘要:前言單例模式是設(shè)計(jì)模式中最簡(jiǎn)單最容易理解的一種,維基百科的定義如下單例模式,也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。 前言 單例模式是設(shè)計(jì)模式(Design Pattern)中最簡(jiǎn)單、最容易理解的一種,維基百科[1]的定義如下: 單例模式,也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類 類 (計(jì)算機(jī)科學(xué)))必須保證只有一個(gè)實(shí)例存在。許多時(shí)候整個(gè)系統(tǒng)只需要擁有一...
摘要:在中,我們可以用多種方法來實(shí)現(xiàn)單例模式使用模塊使用使用裝飾器使用元類使用模塊其實(shí),的模塊就是天然的單例模式,因?yàn)槟K在第一次導(dǎo)入時(shí),會(huì)生成文件,當(dāng)?shù)诙螌?dǎo)入時(shí),就會(huì)直接加載文件,而不會(huì)再次執(zhí)行模塊代碼。 單例模式 單例模式(Singleton Pattern)是一種常用的軟件設(shè)計(jì)模式,該模式的主要目的是確保某一個(gè)類只有一個(gè)實(shí)例存在。當(dāng)你希望在整個(gè)系統(tǒng)中,某個(gè)類只能出現(xiàn)一個(gè)實(shí)例時(shí),單例對(duì)...
摘要:使用元類可以控制類的創(chuàng)建過程,它主要做三件事攔截類的創(chuàng)建修改類的定義返回修改后的類使用元類實(shí)現(xiàn)單例模式的代碼如下執(zhí)行結(jié)果 單例模式 單例模式(Singleton Pattern)是一種常用的軟件設(shè)計(jì)模式,該模式的主要目的是確保某一個(gè)類只有一個(gè)實(shí)例存在。當(dāng)你希望在整個(gè)系統(tǒng)中,某個(gè)類只能出現(xiàn)一個(gè)實(shí)例時(shí),單例對(duì)象就能派上用場(chǎng)。 比如,某個(gè)服務(wù)器程序的配置信息存放在一個(gè)文件中,客戶端通過一個(gè) ...
摘要:博主按每天一個(gè)設(shè)計(jì)模式旨在初步領(lǐng)會(huì)設(shè)計(jì)模式的精髓,目前采用靠這吃飯和純粹喜歡兩種語言實(shí)現(xiàn)。單例模式用途如果一個(gè)類負(fù)責(zé)連接數(shù)據(jù)庫的線程池日志記錄邏輯等等,此時(shí)需要單例模式來保證對(duì)象不被重復(fù)創(chuàng)建,以達(dá)到降低開銷的目的。 博主按:《每天一個(gè)設(shè)計(jì)模式》旨在初步領(lǐng)會(huì)設(shè)計(jì)模式的精髓,目前采用javascript(_靠這吃飯_)和python(_純粹喜歡_)兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)...
閱讀 2515·2021-11-15 11:38
閱讀 1964·2021-11-05 09:37
閱讀 2289·2021-10-08 10:12
閱讀 2821·2019-08-30 15:55
閱讀 2123·2019-08-30 15:52
閱讀 1231·2019-08-29 13:24
閱讀 472·2019-08-26 18:27
閱讀 1485·2019-08-26 18:27