有些時候你的項(xiàng)目中難免需要一些全局唯一的對象,這些對象大多是一些工具性的東西,在Python中實(shí)現(xiàn)單例模式并不是什么難事。以下總結(jié)幾種方法:
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了阿勒泰免費(fèi)建站歡迎大家使用!
使用類裝飾器
使用裝飾器實(shí)現(xiàn)單例類的時候,類本身并不知道自己是單例的,所以寫代碼的人可以不care這個,只要正常寫自己的類的實(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
你會發(fā)現(xiàn)singleton裝飾器內(nèi)部使用了一個dict。當(dāng)然你也可以用其他的方式,不過以下的實(shí)現(xiàn)是錯誤的:
def singleton(cls):
_instance = None #外部作用域的引用對于嵌套的內(nèi)部作用域是只讀的
def _wrapper(*args, **kwargs):
if _instance is None: #解釋器會拋出"UnboundLocalError: ...referenced before assignment"
_instance = cls(*args, **kwargs) #賦值行為使解釋器將"_instance"看作局部變量
return _instance
return _wrapper
使用元類(__metaclass__)和可調(diào)用對象(__call__)
Python的對象系統(tǒng)中一些皆對象,類也不例外,可以稱之為”類型對象”,比較繞,但仔細(xì)思考也不難:類本身也是一種對象,只不過這種對象很特殊,它表示某一種類型。是對象,那必然是實(shí)例化來的,那么誰實(shí)例化后是這種類型對象呢?也就是元類。
Python中,class關(guān)鍵字表示定義一個類對象,此時解釋器會按一定規(guī)則尋找__metaclass__,如果找到了,就調(diào)用對應(yīng)的元類實(shí)現(xiàn)來實(shí)例化該類對象;沒找到,就會調(diào)用type元類來實(shí)例化該類對象。
__call__是Python的魔術(shù)方法,Python的面向?qū)ο笫恰盌uck type”的,意味著對象的行為可以通過實(shí)現(xiàn)協(xié)議來實(shí)現(xiàn),可以看作是一種特殊的接口形式。某個類實(shí)現(xiàn)了__call__方法意味著該類的對象是可調(diào)用的,可以想像函數(shù)調(diào)用的樣子。再考慮一下foo=Foo()這種實(shí)例化的形式,是不是很像啊。結(jié)合元類的概念,可以看出,Foo類是單例的,則在調(diào)用Foo()的時候每次都返回了同樣的對象。而Foo作為一個類對象是單例的,意味著它的類(即生成它的元類)是實(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__來生成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對象的構(gòu)造方法,__init__只負(fù)責(zé)初始化實(shí)例對象,在調(diào)用__init__方法之前,會首先調(diào)用__new__方法生成對象,可以認(rèn)為__new__方法充當(dāng)了構(gòu)造方法的角色。所以可以在__new__中加以控制,使得某個類只生成唯一對象。具體實(shí)現(xiàn)時可以實(shí)現(xiàn)一個父類,重載__new__方法,單例類只需要繼承這個父類就好。
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
在聊這之前我們首先要明確的是,單例模式在實(shí)際中的意義以及在python中具有實(shí)現(xiàn)的價值?
當(dāng)前,相信有很多人支持單例模式,也有不少人反對,尤其是在python中,目前依舊具有很大的爭議性。我們要在評論之前首先要了解單例模式
什么是單例模式?
顧名思義:就是單個模式
單例模式是一種常見的軟件設(shè)置模式,在它的核心結(jié)構(gòu)中只包含一個被稱為單例類的特殊類,通過單例模式可以保證系統(tǒng)中的一個類只有一個實(shí)例而且該實(shí)例易于外界訪問,從而方便對實(shí)例個數(shù)的控制并節(jié)約系統(tǒng)資源。如果希望在系統(tǒng)中某個對象只能存在一個,單例模式是最好的解決方案。
單例模式的要點(diǎn)有三類
某個類只能有一個實(shí)例
它必須創(chuàng)建這個實(shí)例
它必須自行向整個系統(tǒng)提供這個實(shí)例
但是從具體角度實(shí)現(xiàn)來說的話,又可以分為三點(diǎn)
單例模式的類只能提供私有的構(gòu)造函數(shù)
類定義中含有一個該類的靜態(tài)私有對象
該類提供了一個靜態(tài)的共有的函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對象
一、實(shí)例控制
單例模式會阻止其他對象實(shí)例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實(shí)例。
二、靈活性
因?yàn)轭惪刂屏藢?shí)例化過程,所以類可以靈活更改實(shí)例化過程。
缺點(diǎn):
一、開銷
雖然數(shù)量很少,但如果每次對象請求引用時都要檢查是否存在類的實(shí)例,將仍然需要一些開銷??梢酝ㄟ^使用靜態(tài)初始化解決此問題。
二、可能的開發(fā)混淆
使用單例對象(尤其在類庫中定義的對象)時,開發(fā)人員必須記住自己不能使用?new關(guān)鍵字實(shí)例化對象。因?yàn)榭赡軣o法訪問庫源代碼,因此應(yīng)用程序開發(fā)人員可能會意外發(fā)現(xiàn)自己無法直接實(shí)例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內(nèi)存管理的語言中(例如基于.NET Framework的語言),只有單例類能夠?qū)е聦?shí)例被取消分配,因?yàn)樗瑢υ搶?shí)例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實(shí)例,但這樣會導(dǎo)致單例類中出現(xià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界面時,單例模式的簡單運(yùn)用
web 單例模式
不過我們需要注意的是:
特殊方法__new__是一個元構(gòu)造程序,每當(dāng)一個對象必須被factory類實(shí)例化時,就將調(diào)用它。__new__方法必須返回一個類的實(shí)例,因此它可以在對象創(chuàng)建之前或之后修改類。
因?yàn)開_init__在子類中不會被隱式調(diào)用,所以__new__可以用來確定已經(jīng)在整個類層次完成了初始化構(gòu)造。__new__是對于對象狀態(tài)隱式初始化需求的回應(yīng),使得可以在比__init__更低的一個層次上定義一個初始化,這個初始化總是會被調(diào)用。
與__init__()相比__new__()方法更像一個真正的構(gòu)造器。隨著類和類型的統(tǒng)一,用戶可以對內(nèi)建類型進(jìn)行派生,因此需要一種途徑來實(shí)例化不可變對象,比如派生字符串,在這種情況下解釋器則調(diào)用類的__new__()方法,一個靜態(tài)方法,并且傳入的參數(shù)是在類實(shí)例化操作時生成的。__new__()會調(diào)用父類的__new__()來創(chuàng)建對象(向上代理)
·__new__必須返回一個合法的實(shí)例,這樣解釋器在調(diào)用__init__()時,就可以吧這個實(shí)例作為self傳給他。調(diào)用父類的__new__()來創(chuàng)建對象,正向其他語言使用new關(guān)鍵字一樣
總結(jié)
單利模式存在的目的是保證當(dāng)前內(nèi)存中僅存在單個實(shí)例,避免內(nèi)存浪費(fèi)?。?!
一。單例模式
一般情況下,類可以生成任意個實(shí)例,而單例模式只生成一個實(shí)例
我們先用單例模式設(shè)計(jì)一個Rectangle類
然后用__new__方法設(shè)計(jì)單例模式,代碼如下
然后我們來驗(yàn)證下,單例模式下是否只能生成一個實(shí)例
單例模式在程序設(shè)計(jì)中比較典型的應(yīng)用場景:多個用戶同時調(diào)用某個模塊時,會生成一些日志,我們希望這些日志存在同一個文件內(nèi),而不是多個文件。
在生成日志模塊我們就可以采用單例模式進(jìn)行設(shè)計(jì)。
二。反射
概念:簡單來說就是可以利用字符串來映射模塊中的相應(yīng)方法然后可以操作模塊中相應(yīng)的方法
我們以一個飯店點(diǎn)菜的實(shí)際場景來理解Python的反射機(jī)制
hasatter(對象,屬性或方法名)
判斷對象中是否有某個屬性或某個方法,返回值是布爾型
getattr(對象,屬性或方法名,缺省值) 判斷對象中是否有某個屬性或某個方法,如果有返回方法本身,沒有則返回缺省值
setattr(對象,屬性,新值)
將實(shí)例的屬性改為新的值,如果屬性不存在則新建
我們給實(shí)例guke1加個價格屬性