Python設計模式——單例模式

else發表於2021-09-09

一、單例模式理論

單例模式:
    保證某一個類只有一個例項,而且在全域性只有一個訪問點
優點:
    1、由於單例模式要求在全域性內只有一個例項,因而可以節省比較多的記憶體空間
    2、全域性只有一個接入點,可以更好地進行資料同步控制,避免多重佔用
    3、單例可長駐記憶體,減少系統開銷

缺點:
    1、單例模式的擴充套件是比較困難的
    2、賦予了單例模式太多的職責,某種程度上違反了單一職責原則(六大設計原則之一)
    3、單例模式是併發協作軟體模組中需要最先完成的,因而其不利於測試
    4、單例模式在某種情況下會導致資源瓶頸

應用場景:
    1、生成全域性唯一的變數,比如序列號
    2、訪問全域性複用的唯一資源,如磁碟,匯流排等
    3、單個物件佔用的資源太多,如資料庫等
    4、系統全域性統一管理,如windows下的工作管理員

二、對__new__的分析

# class Demo:

#     def __init__(self, name):

#         self.name = name

#

#     def show_name(self):

#         print("Name is: {}".format(self.name))

#

# if __name__ == '__main__':

#

#     d = Demo("toby")

#     d.show_name()

 

'''

引用大神的描述:

    這樣便是__init__最普通的用法了。但__init__其實不是例項化一個類的時候第一個被呼叫的方法。

    當使用Demo("toby")這樣的表示式來例項化一個類時,最先被呼叫的方法其實是 __new__ 方法。

    __new__ 方法是什麼?

    __new__方法接受的引數雖然也是和__init__一樣,但__init__是在類例項建立之後呼叫,而__new__方法正是建立這個類例項的方法。

'''

 

class Demo:

    def __new__(cls, name):

        print("Call __new__")

        class_instance = super(Demo, cls).__new__(cls)

        print("new建立了類例項:", class_instance) #列印一下

        return class_instance #返回Demon類的一個例項給__init__,然後利用這個例項來呼叫類的__init__方法

 

    def __init__(self, name): #那__init__用什麼來接收__new__返回來的類例項呢?答案就是self

        print("init接收到new返回回來的類例項:", self) #列印一下這個self,也就是看一下由__new__產生的例項

        print("Call __init__")

        self.name = name

 

    def show_name(self):

        print("Name is: {}".format(self.name))

#

# if __name__ == '__main__':

#     d = Demo("toby")

#     d.show_name()

 

'''

引用大神的總結:

    所以,__init__ 和 __new__ 最主要的區別在於:

    1、__init__ 通常用於初始化一個新例項,控制這個初始化的過程,比如新增一些屬性, 

    做一些額外的操作,發生在類例項被建立完以後。它是例項級別的方法。

    2、__new__ 通常用於控制生成一個新例項的過程。它是類級別的方法。

    但是說了這麼多,__new__最通常的用法是什麼呢,我們什麼時候需要__new__?,單例模式就是一個很好的例子

'''

三、單例模式(案例1)

#coding:utf-8

import threading

import time

 

#這裡使用方法__new__來實現單例模式

class Singleton(object): #抽象單例

    def __new__(cls, *args, **kwargs):

        if not hasattr(cls, "_instance"):

            orig = super(Singleton, cls)

            cls._instance = orig.__new__(cls, *args, **kwargs)

        return cls._instance

 

#匯流排

class Bus(Singleton):

    lock = threading.RLock()

    def sendData(self, data):

        self.lock.acquire()

        time.sleep(3)

        print("Sending Signal Data...", data)

        self.lock.release()

 

#執行緒物件,為更加說明單例的含義,這裡講Bus物件例項化在了run裡

class VisitEntity(threading.Thread):

    my_bus = ""

    name = ""

    def getName(self):

        return self.name

    def setName(self, name):

        self.name = name

    def run(self):

        self.my_bus = Bus()

        self.my_bus.sendData(self.name)

if __name__ == '__main__':

    for i in range(3):

        print("Entity {} begin to run...".format(i))

        v = VisitEntity()

        v.setName("Toby"+str(i))

        v.start()


四、單例模式(案例2)

#使用__new__類方法來保證只有一個例項被建立出來

class OneOnly(object):

    _singleton = None

    def __new__(cls, *args, **kwargs):

        if not cls._singleton: #判斷是否建立有例項,如果沒有則呼叫super()函式來建立它

            cls._singleton = super(OneOnly, cls).__new__(cls) #構造方法__new__被呼叫時,會構造該類的一個新例項

        return cls._singleton

 

#定義一個普通的類,僅有初始化方法__init__

class Test:

    def __init__(self, name):

        self.name = name

 

#定義一個Demo類,並繼承實現單例模式的OneOnly類

class Demo(OneOnly):

    def __init__(self, name):

        self.name = name

 

    def chage_name(self, new_name):

        self.name = new_name

 

    def show_name(self):

        print("Name is: {}".format(self.name))

 

if __name__ == '__main__':

 

    #當透過OneOnly類中的構造方法構造例項時,總是會的得到完全相同的例項,記憶體地址也相同

    # o1 = OneOnly()

    # o2 = OneOnly()

    # print(o1)

    # print(o2)

 

    #透過Test類建立出來的兩個例項t1和t2,是兩個不同的例項,記憶體地址也是不同的

    # t1 = Test("toby")

    # t2 = Test("ttr")

    # print(t1)

    # print(t2)

 

    #Demo類繼承了實現了單例模式的OneOnly類,因此,透過Demo類例項化出來的物件都是同一個物件

    d1 = Demo("toby")

    d2 = Demo("ttr")

    d3 = Demo("laowang")

    # 透過列印可知,他們的記憶體地址都是一樣的

    # print(d1)

    # print(d2)

 

    #發現列印出來的name都是"laowang",似乎最後建立的一個例項把前兩個的給覆蓋了

    d1.show_name()

    d2.show_name()

    d3.show_name()

 

    #透過例項d1修改名字後,再透過例項d3和d2來檢視,果然也一起改變了。由此證明他們確實是同一個例項

    d1.chage_name("juck")

    d3.show_name()

    d2.show_name()


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2318/viewspace-2809934/,如需轉載,請註明出處,否則將追究法律責任。

相關文章