十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
單例模式就是 : 1、類的構(gòu)造函數(shù)為private,即外部程序不能通過(guò)new關(guān)鍵字創(chuàng)建對(duì)象的實(shí)例 2、類中提供一個(gè)private static的 類變量引用 ; 3、單例類中提供靜態(tài)方法 定義為 public static 的方法獲取一個(gè)類的實(shí)例 ; 4、靜態(tài)方法返回 類的引用,...
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、龍湖ssl等。為近1000家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的龍湖網(wǎng)站制作公司
在聊這之前我們首先要明確的是,單例模式在實(shí)際中的意義以及在python中具有實(shí)現(xiàn)的價(jià)值?
當(dāng)前,相信有很多人支持單例模式,也有不少人反對(duì),尤其是在python中,目前依舊具有很大的爭(zhēng)議性。我們要在評(píng)論之前首先要了解單例模式
什么是單例模式?
顧名思義:就是單個(gè)模式
單例模式是一種常見(jiàn)的軟件設(shè)置模式,在它的核心結(jié)構(gòu)中只包含一個(gè)被稱為單例類的特殊類,通過(guò)單例模式可以保證系統(tǒng)中的一個(gè)類只有一個(gè)實(shí)例而且該實(shí)例易于外界訪問(wèn),從而方便對(duì)實(shí)例個(gè)數(shù)的控制并節(jié)約系統(tǒng)資源。如果希望在系統(tǒng)中某個(gè)對(duì)象只能存在一個(gè),單例模式是最好的解決方案。
單例模式的要點(diǎn)有三類
某個(gè)類只能有一個(gè)實(shí)例
它必須創(chuàng)建這個(gè)實(shí)例
它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例
但是從具體角度實(shí)現(xiàn)來(lái)說(shuō)的話,又可以分為三點(diǎn)
單例模式的類只能提供私有的構(gòu)造函數(shù)
類定義中含有一個(gè)該類的靜態(tài)私有對(duì)象
該類提供了一個(gè)靜態(tài)的共有的函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對(duì)象
一、實(shí)例控制
單例模式會(huì)阻止其他對(duì)象實(shí)例化其自己的單例對(duì)象的副本,從而確保所有對(duì)象都訪問(wèn)唯一實(shí)例。
二、靈活性
因?yàn)轭惪刂屏藢?shí)例化過(guò)程,所以類可以靈活更改實(shí)例化過(guò)程。
缺點(diǎn):
一、開(kāi)銷
雖然數(shù)量很少,但如果每次對(duì)象請(qǐng)求引用時(shí)都要檢查是否存在類的實(shí)例,將仍然需要一些開(kāi)銷。可以通過(guò)使用靜態(tài)初始化解決此問(wèn)題。
二、可能的開(kāi)發(fā)混淆
使用單例對(duì)象(尤其在類庫(kù)中定義的對(duì)象)時(shí),開(kāi)發(fā)人員必須記住自己不能使用?new關(guān)鍵字實(shí)例化對(duì)象。因?yàn)榭赡軣o(wú)法訪問(wèn)庫(kù)源代碼,因此應(yīng)用程序開(kāi)發(fā)人員可能會(huì)意外發(fā)現(xiàn)自己無(wú)法直接實(shí)例化此類。
三、對(duì)象生存期
不能解決刪除單個(gè)對(duì)象的問(wèn)題。在提供內(nèi)存管理的語(yǔ)言中(例如基于.NET Framework的語(yǔ)言),只有單例類能夠?qū)е聦?shí)例被取消分配,因?yàn)樗瑢?duì)該實(shí)例的私有引用。在某些語(yǔ)言中(如 C++),其他類可以刪除對(duì)象實(shí)例,但這樣會(huì)導(dǎo)致單例類中出現(xiàn)懸浮引用。
常用幾種方式
通過(guò)面向的特性,簡(jiǎn)單的構(gòu)造出單例模式
123456789101112131415? ?# ########### 單例類定義 ###########class?Foo(object):??????__instance?=?None??????@staticmethod????def?singleton():????????if?Foo.__instance:????????????return?Foo.__instance????????else:????????????Foo.__instance?=?Foo()????????????return?Foo.__instance??# ########### 獲取實(shí)例 ###########obj?=?Foo.singleton()? ?
當(dāng)用于WEB界面時(shí),單例模式的簡(jiǎn)單運(yùn)用
web 單例模式
不過(guò)我們需要注意的是:
特殊方法__new__是一個(gè)元構(gòu)造程序,每當(dāng)一個(gè)對(duì)象必須被factory類實(shí)例化時(shí),就將調(diào)用它。__new__方法必須返回一個(gè)類的實(shí)例,因此它可以在對(duì)象創(chuàng)建之前或之后修改類。
因?yàn)開(kāi)_init__在子類中不會(huì)被隱式調(diào)用,所以__new__可以用來(lái)確定已經(jīng)在整個(gè)類層次完成了初始化構(gòu)造。__new__是對(duì)于對(duì)象狀態(tài)隱式初始化需求的回應(yīng),使得可以在比__init__更低的一個(gè)層次上定義一個(gè)初始化,這個(gè)初始化總是會(huì)被調(diào)用。
與__init__()相比__new__()方法更像一個(gè)真正的構(gòu)造器。隨著類和類型的統(tǒng)一,用戶可以對(duì)內(nèi)建類型進(jìn)行派生,因此需要一種途徑來(lái)實(shí)例化不可變對(duì)象,比如派生字符串,在這種情況下解釋器則調(diào)用類的__new__()方法,一個(gè)靜態(tài)方法,并且傳入的參數(shù)是在類實(shí)例化操作時(shí)生成的。__new__()會(huì)調(diào)用父類的__new__()來(lái)創(chuàng)建對(duì)象(向上代理)
·__new__必須返回一個(gè)合法的實(shí)例,這樣解釋器在調(diào)用__init__()時(shí),就可以吧這個(gè)實(shí)例作為self傳給他。調(diào)用父類的__new__()來(lái)創(chuàng)建對(duì)象,正向其他語(yǔ)言使用new關(guān)鍵字一樣
總結(jié)
單利模式存在的目的是保證當(dāng)前內(nèi)存中僅存在單個(gè)實(shí)例,避免內(nèi)存浪費(fèi)!?。?/p>
# mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
將上面的代碼保存在文件 mysingleton.py 中,然后這樣使用:
from mysingleton import my_singleton
有些時(shí)候你的項(xiàng)目中難免需要一些全局唯一的對(duì)象,這些對(duì)象大多是一些工具性的東西,在Python中實(shí)現(xiàn)單例模式并不是什么難事。以下總結(jié)幾種方法:
使用類裝飾器
使用裝飾器實(shí)現(xiàn)單例類的時(shí)候,類本身并不知道自己是單例的,所以寫(xiě)代碼的人可以不care這個(gè),只要正常寫(xiě)自己的類的實(shí)現(xiàn)就可以,類的單例有裝飾器保證。
def singleton(cls):
instances = {}
def _wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return _wrapper
你會(huì)發(fā)現(xiàn)singleton裝飾器內(nèi)部使用了一個(gè)dict。當(dāng)然你也可以用其他的方式,不過(guò)以下的實(shí)現(xiàn)是錯(cuò)誤的:
def singleton(cls):
_instance = None #外部作用域的引用對(duì)于嵌套的內(nèi)部作用域是只讀的
def _wrapper(*args, **kwargs):
if _instance is None: #解釋器會(huì)拋出"UnboundLocalError: ...referenced before assignment"
_instance = cls(*args, **kwargs) #賦值行為使解釋器將"_instance"看作局部變量
return _instance
return _wrapper
使用元類(__metaclass__)和可調(diào)用對(duì)象(__call__)
Python的對(duì)象系統(tǒng)中一些皆對(duì)象,類也不例外,可以稱之為”類型對(duì)象”,比較繞,但仔細(xì)思考也不難:類本身也是一種對(duì)象,只不過(guò)這種對(duì)象很特殊,它表示某一種類型。是對(duì)象,那必然是實(shí)例化來(lái)的,那么誰(shuí)實(shí)例化后是這種類型對(duì)象呢?也就是元類。
Python中,class關(guān)鍵字表示定義一個(gè)類對(duì)象,此時(shí)解釋器會(huì)按一定規(guī)則尋找__metaclass__,如果找到了,就調(diào)用對(duì)應(yīng)的元類實(shí)現(xiàn)來(lái)實(shí)例化該類對(duì)象;沒(méi)找到,就會(huì)調(diào)用type元類來(lái)實(shí)例化該類對(duì)象。
__call__是Python的魔術(shù)方法,Python的面向?qū)ο笫恰盌uck type”的,意味著對(duì)象的行為可以通過(guò)實(shí)現(xiàn)協(xié)議來(lái)實(shí)現(xiàn),可以看作是一種特殊的接口形式。某個(gè)類實(shí)現(xiàn)了__call__方法意味著該類的對(duì)象是可調(diào)用的,可以想像函數(shù)調(diào)用的樣子。再考慮一下foo=Foo()這種實(shí)例化的形式,是不是很像啊。結(jié)合元類的概念,可以看出,Foo類是單例的,則在調(diào)用Foo()的時(shí)候每次都返回了同樣的對(duì)象。而Foo作為一個(gè)類對(duì)象是單例的,意味著它的類(即生成它的元類)是實(shí)現(xiàn)了__call__方法的。所以可以如下實(shí)現(xiàn):
class Singleton(type):
def __init__(cls, name, bases, attrs):
super(Singleton, cls).__init__(name, bases, attrs)
cls._instance = None
def __call__(cls, *args, **kwargs):
if cls._instance is None
# 以下不要使用'cls._instance = cls(*args, **kwargs)', 防止死循環(huán),
# cls的調(diào)用行為已經(jīng)被當(dāng)前'__call__'協(xié)議攔截了
# 使用super(Singleton, cls).__call__來(lái)生成cls的實(shí)例
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
class Foo(object): #單例類
__metaclass__ = Singleton
a = Foo()
b = Foo()
a is b
True
a.x = 1
b.x
1
使用__new__
__init__不是Python對(duì)象的構(gòu)造方法,__init__只負(fù)責(zé)初始化實(shí)例對(duì)象,在調(diào)用__init__方法之前,會(huì)首先調(diào)用__new__方法生成對(duì)象,可以認(rèn)為_(kāi)_new__方法充當(dāng)了構(gòu)造方法的角色。所以可以在__new__中加以控制,使得某個(gè)類只生成唯一對(duì)象。具體實(shí)現(xiàn)時(shí)可以實(shí)現(xiàn)一個(gè)父類,重載__new__方法,單例類只需要繼承這個(gè)父類就好。
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
class Foo(Singleton): #單例類
a = 1