python 創(chuàng)建單例模式的五種方法

一改艇、使用模塊

其實(shí),Python 的模塊就是天然的單例模式研儒,因?yàn)槟K在第一次導(dǎo)入時(shí)豫缨,會(huì)生成 .pyc 文件,當(dāng)?shù)诙螌?dǎo)入時(shí)端朵,就會(huì)直接加載 .pyc 文件好芭,而不會(huì)再次執(zhí)行模塊代碼。因此冲呢,我們只需把相關(guān)的函數(shù)和數(shù)據(jù)定義在一個(gè)模塊中舍败,就可以獲得一個(gè)單例對(duì)象了。如果我們真的想要一個(gè)單例類敬拓,可以考慮這樣做:

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass
 
my_singleton = My_Singleton()

將上面的代碼保存在文件 mysingleton.py 中邻薯,然后這樣使用:

from mysingleton import my_singleton
 
my_singleton.foo()

二、使用裝飾器(decorator)

個(gè)人喜歡使用這個(gè)

def singleton(cls, *args, **kw):  
    instances = {}  
    def _singleton():  
        if cls not in instances:  
            instances[cls] = cls(*args, **kw)  
        return instances[cls]  
    return _singleton  
 
@singleton  
class MyClass(object):  
    a = 1  
    def __init__(self, x=0):  
        self.x = x  
  
one = MyClass()  
two = MyClass()  
  
two.a = 3  
print one.a  
#3  
print id(one)  
#29660784  
print id(two)  
#29660784  
print one == two  
#True  
print one is two  
#True  
one.x = 1  
print one.x  
#1  
print two.x
#1

三恩尾、使用類

class Singleton(object):

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

import threading

def task(arg):
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

一般情況弛说,大家以為這樣就完成了單例模式,但是這樣當(dāng)使用多線程時(shí)會(huì)存在問題翰意。

程序執(zhí)行后木人,打印結(jié)果如下:

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

看起來也沒有問題信柿,那是因?yàn)閳?zhí)行速度過快,如果在init方法中有一些IO操作醒第,就會(huì)發(fā)現(xiàn)問題了渔嚷,下面我們通過time.sleep模擬

我們?cè)谏厦?strong>init方法中加入以下代碼:

def __init__(self):
        import time
        time.sleep(1)

重新執(zhí)行程序后,結(jié)果如下:

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

問題出現(xiàn)了稠曼!按照以上方式創(chuàng)建的單例形病,無法支持多線程
解決辦法:加鎖!未加鎖部分并發(fā)執(zhí)行,加鎖部分串行執(zhí)行,速度降低,但是保證了數(shù)據(jù)安全

import time
import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        time.sleep(1)

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):#這個(gè)作用是優(yōu)化判斷霞幅,預(yù)防已經(jīng)實(shí)例化了就不必進(jìn)入那段代碼睡眠
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance()
    print(obj)
for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

完整代碼

打印結(jié)果如下:

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

這種方式實(shí)現(xiàn)的單例模式漠吻,使用時(shí)會(huì)有限制,以后實(shí)例化必須通過 obj = Singleton.instance()
如果用 obj=Singleton() ,這種方式得到的不是單例

四司恳、基于new方法實(shí)現(xiàn)(推薦使用途乃,方便)

通過上面例子,我們可以知道扔傅,當(dāng)我們實(shí)現(xiàn)單例時(shí)耍共,為了保證線程安全需要在內(nèi)部加入鎖。
我們知道猎塞,當(dāng)我們實(shí)例化一個(gè)對(duì)象時(shí)试读,是先執(zhí)行了類的new方法(我們沒寫時(shí),默認(rèn)調(diào)用object.new)荠耽,實(shí)例化對(duì)象钩骇;然后再執(zhí)行類的init方法,對(duì)這個(gè)對(duì)象進(jìn)行初始化骇塘,所有我們可以基于這個(gè)伊履,實(shí)現(xiàn)單例模式韩容。

import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = object.__new__(cls)  
        return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

打印結(jié)果如下:

<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>

上面的代碼方法使用__new__的同時(shí)還要通過加鎖才能實(shí)現(xiàn)單例款违,下面還有一個(gè)可以不加鎖來實(shí)現(xiàn),不過要先初化一個(gè)屬性值群凶。

簡化版:
class Singleton(object):
    __instance = None
    def __init__(self,name,age):
        if self.__first_init:
            self.name = name
            self.age = age
    
    def __new__(cls,*args,**kwargs):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance
 
a = Singleton('a',12)
b = Singleton('b',13)
print(a)
print(b)
 
print(a.name)
print(b.name)

采用這種方式的單例模式插爹,以后實(shí)例化對(duì)象時(shí),和平時(shí)實(shí)例化對(duì)象的方法一樣 obj = Singleton()

五请梢、基于metaclass方式實(shí)現(xiàn)

1.類由type創(chuàng)建赠尾,創(chuàng)建類時(shí),type的__init__方法自動(dòng)執(zhí)行毅弧,類() 執(zhí)行type的 __call__方法(類的__new__方法,類的__init__方法)
2.對(duì)象由類創(chuàng)建气嫁,創(chuàng)建對(duì)象時(shí),類的__init__方法自動(dòng)執(zhí)行够坐,對(duì)象()執(zhí)行類的 __call__方法

import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寸宵,一起剝皮案震驚了整個(gè)濱河市崖面,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梯影,老刑警劉巖巫员,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甲棍,居然都是意外死亡简识,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門感猛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來七扰,“玉大人,你說我怎么就攤上這事陪白〈链纾” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵拷泽,是天一觀的道長疫鹊。 經(jīng)常有香客問我,道長司致,這世上最難降的妖魔是什么拆吆? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮脂矫,結(jié)果婚禮上枣耀,老公的妹妹穿的比我還像新娘。我一直安慰自己庭再,他們只是感情好捞奕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拄轻,像睡著了一般颅围。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恨搓,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天院促,我揣著相機(jī)與錄音,去河邊找鬼斧抱。 笑死常拓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辉浦。 我是一名探鬼主播弄抬,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼宪郊!你這毒婦竟也來了掂恕?” 一聲冷哼從身側(cè)響起荔茬,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竹海,沒想到半個(gè)月后慕蔚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斋配,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年孔飒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艰争。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坏瞄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甩卓,到底是詐尸還是另有隱情鸠匀,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布逾柿,位于F島的核電站缀棍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏机错。R本人自食惡果不足惜爬范,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弱匪。 院中可真熱鬧青瀑,春花似錦、人聲如沸萧诫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帘饶。三九已至哑诊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尖奔,已是汗流浹背搭儒。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留提茁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓馁菜,卻偏偏與公主長得像茴扁,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子汪疮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • 〇峭火、前言 本文共108張圖毁习,流量黨請(qǐng)慎重! 歷時(shí)1個(gè)半月卖丸,我把自己學(xué)習(xí)Python基礎(chǔ)知識(shí)的框架詳細(xì)梳理了一遍纺且。 ...
    Raxxie閱讀 18,957評(píng)論 17 410
  • 未來,人類將面臨著三大問題:生物本身其實(shí)就是算 法稍浆,生命是不斷處理數(shù)據(jù)的過程:载碌;意識(shí)與智能的分離 擁有大數(shù)據(jù)積累的...
    為人子要孝順閱讀 237評(píng)論 0 0
  • 在我們的人生中嫁艇,總是不斷學(xué)會(huì)如何去爭取,卻很少有人教我們?nèi)绾稳シ艞墶?從讀書時(shí)的成績到工作后的事業(yè)弦撩,我們知道很多事...
    A沈健閱讀 762評(píng)論 3 2
  • 孤獨(dú)的站立著 拉長了行色匆匆 等待著夜色朦朧 點(diǎn)亮孤獨(dú) 守望著寂寞 卻把沉默點(diǎn)燃 晨曦中的曙光 是下一次等待的開始
    瑞德堂主88閱讀 445評(píng)論 0 1