摘要:實現(xiàn)實現(xiàn)單例模式有多種方案使用提供了非常易用的類,只要繼承它,就會成為單例。參考鏈接單例模式最后,感謝女朋友支持。
問題:現(xiàn)代化的巧克力工廠具備計算機控制的巧克力鍋爐。鍋爐做的事情就是把巧克力和牛奶融在一起,然后送到下一個階段,以制成巧克力棒。下邊是一個巧克力公司鍋爐控制器的代碼,仔細觀察一下,這段代碼有什么問題?
class ChocolateBoiler(object): def __init__(self): self.empty = True self.boiled = False def fill(self): # 向鍋爐填充巧克力和牛奶混合物 # 在鍋爐內(nèi)填充原料時,鍋爐必須是空的。 # 一旦填入原料,就要把empty 和 boiled 標志設置好 if self.empty: self.empty = False self.boiled = False def drain(self): # 排出煮沸的巧克力和牛奶 # 鍋爐排出時,必須是滿的且煮沸的。 # 排出完畢empty 設置為 true if not self.empty and self.boiled: self.empty = True def boil(self): # 將顱內(nèi)物煮沸 # 煮混合物時,鍋爐內(nèi)必須是滿的且沒有煮沸過 # 一旦煮沸,就把 boiled 設置為 true if not self.empty and not self.boiled: self.boiled = True
從代碼可以看出,他們加入了多種判斷,以防止不好的事情發(fā)生。如果同時存在兩個ChocolateBoiler實例,那這么多判斷豈不是失去作用了。那我們改如何實現(xiàn)這個需求呢?這個問題的核心是,我們要先判斷實例是不是已經(jīng)存在,如果存在就不再創(chuàng)建。
_chocolate_boiler_instance = None # 聲明實例 def chocolate_boiler(): global _chocolate_boiler_instance # 使用全局變量 if _chocolate_boiler_instance is not None: # 判斷是否存在,如果存在,直接返回 return _chocolate_boiler_instance else: # 如果不存在,創(chuàng)建一個新的 _chocolate_boiler_instance = ChocolateBoiler() return _chocolate_boiler_instance
現(xiàn)在我們需要獲取 ChocolateBoiler 實例的時候只需要調(diào)用 chocolate_boiler 方法獲取實例即可保證同時只有一個 ChocolateBoiler實例。
這種保證 ChocolateBoiler類只有一個實例,并提供一個全局訪問點的模式,就是單例模式。
單例模式 定義單例模式:確保一個類只有一個實例,并提供一個全局訪問點。
也就是說,我們使用單例模式要把某個類設計成自己管理的一個多帶帶實例,同時也避免其他類再自行產(chǎn)生實例。并且只允許通過單例類獲取單例的實例。
我們也提供對這個實例的全局訪問點:當你需要實例時,像類查詢,它會返回單個實例。
實現(xiàn)python 實現(xiàn)單例模式有多種方案:
使用 metaclass《python cookbook》提供了非常易用的 Singleton 類,只要繼承它,就會成為單例。
# python 3 代碼實現(xiàn) class Singleton(type): def __init__(self, *args, **kwargs): self.__instance = None super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self.__instance is None: # 如果 __instance 不存在,創(chuàng)建新的實例 self.__instance = super().__call__(*args, **kwargs) return self.__instance else: # 如果存在,直接返回 return self.__instance class Spam(metaclass=Singleton): def __init__(self): print("Creating Spam") a = Spam() b = Spam() print(a is b) # 這里輸出為 True
元類(metaclass)可以控制類的創(chuàng)建過程,它主要做三件事:
攔截類的創(chuàng)建
修改類的定義
返回修改后的類
例子中我們構造了一個Singleton元類,并使用__call__方法使其能夠模擬函數(shù)的行為。構造類 Spam 時,將其元類設為Singleton,那么創(chuàng)建類對象 Spam 時,行為發(fā)生如下:
Spam = Singleton(name,bases,class_dict),Spam 其實為Singleton類的一個實例。
創(chuàng)建 Spam 的實例時,Spam()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__(),這樣就將 Spam 的所有實例都指向了 Spam 的屬性 __instance上。
使用 new我們可以使用 new 來控制實例的創(chuàng)建過程,代碼如下:
class Singleton(object): __instance = None def __new__(cls, *args, **kw): if not cls.__instance: cls.__instance = super().__new__(cls, *args, **kw) return cls.__instance class Foo(Singleton): a = 1 one = Foo() two = Foo() assert one == two assert one is two assert id(one) == id(two)
通過 new 方法,將類的實例在創(chuàng)建的時候綁定到類屬性 __instance 上。如果cls.__instance 為None,說明類還未實例化,實例化并將實例綁定到cls.__instance 以后每次實例化的時候都返回第一次實例化創(chuàng)建的實例。注意從Singleton派生子類的時候,不要重載__new__。
使用裝飾器import functools def singleton(cls): """ Use class as singleton. """ # 首先將 __new__ 方法賦值給 __new_original__ cls.__new_original__ = cls.__new__ @functools.wraps(cls.__new__) def singleton_new(cls, *args, **kw): # 嘗試從 __dict__ 取 __it__ it = cls.__dict__.get("__it__") if it is not None: # 如果有值,說明實例已經(jīng)創(chuàng)建,返回實例 return it # 如果實例不存在,使用 __new_original__ 創(chuàng)建實例,并將實例賦值給 __it__ cls.__it__ = it = cls.__new_original__(cls, *args, **kw) it.__init_original__(*args, **kw) return it # class 將原有__new__ 方法用 singleton_new 替換 cls.__new__ = singleton_new cls.__init_original__ = cls.__init__ cls.__init__ = object.__init__ return cls # # 使用示例 # @singleton class Foo: def __new__(cls): cls.x = 10 return object.__new__(cls) def __init__(self): assert self.x == 10 self.x = 15 assert Foo().x == 15 Foo().x = 20 assert Foo().x == 20
這種方法的內(nèi)部實現(xiàn)和使用 __new__ 類似:
首先,將 new 方法賦值給 new_original__,原有 new 方法用 singleton_new 替換,定義 init_original 并將 cls.__init 賦值給 init_original
在 singleton_new 方法內(nèi)部,嘗試從 dict 取 __it__(實例)
如果實例不存在,使用 new_original 創(chuàng)建實例,并將實例賦值給 __it__,然后返回實例
最簡單的方式將名字singleton綁定到實例上,singleton就是它自己類的唯一對象了。
class singleton(object): pass singleton = singleton()
https://github.com/gusibi/Metis/blob/master/apis/v1/schemas.py#L107 使用的就是這種方式,用來獲取全局的 request
參考鏈接Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc 文件,當?shù)诙螌霑r,就會直接加載 .pyc 文件,而不會再次執(zhí)行模塊代碼。因此,我們只需把相關的函數(shù)和數(shù)據(jù)定義在一個模塊中,就可以獲得一個單例對象了。
Creating a singleton in Python
Python單例模式
Why is __init__() always called after __new__()?
最后,感謝女朋友支持。
歡迎關注(April_Louisa) | 請我喝芬達 |
---|---|
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/44499.html
摘要:本篇文章總結了目前主流的實現(xiàn)單例模式的方法供讀者參考。使用實現(xiàn)單例模式同樣,我們在類的創(chuàng)建時進行干預,從而達到實現(xiàn)單例的目的。 很多初學者喜歡用 全局變量 ,因為這比函數(shù)的參數(shù)傳來傳去更容易讓人理解。確實在很多場景下用全局變量很方便。不過如果代碼規(guī)模增大,并且有多個文件的時候,全局變量就會變得比較混亂。你可能不知道在哪個文件中定義了相同類型甚至重名的全局變量,也不知道這個變量在程序的某...
摘要:前言單例模式是設計模式中最簡單最容易理解的一種,維基百科的定義如下單例模式,也叫單子模式,是一種常用的軟件設計模式。 前言 單例模式是設計模式(Design Pattern)中最簡單、最容易理解的一種,維基百科[1]的定義如下: 單例模式,也叫單子模式,是一種常用的軟件設計模式。在應用這個模式時,單例對象的類 類 (計算機科學))必須保證只有一個實例存在。許多時候整個系統(tǒng)只需要擁有一...
摘要:在中,我們可以用多種方法來實現(xiàn)單例模式使用模塊使用使用裝飾器使用元類使用模塊其實,的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成文件,當?shù)诙螌霑r,就會直接加載文件,而不會再次執(zhí)行模塊代碼。 單例模式 單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統(tǒng)中,某個類只能出現(xiàn)一個實例時,單例對...
摘要:使用元類可以控制類的創(chuàng)建過程,它主要做三件事攔截類的創(chuàng)建修改類的定義返回修改后的類使用元類實現(xiàn)單例模式的代碼如下執(zhí)行結果 單例模式 單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統(tǒng)中,某個類只能出現(xiàn)一個實例時,單例對象就能派上用場。 比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 ...
摘要:簡單工廠模式工廠模式有一種非常形象的描述,建立對象的類就如一個工廠,而需要被建立的對象就是一個個產(chǎn)品在工廠中加工產(chǎn)品,使用產(chǎn)品的人,不用在乎產(chǎn)品是如何生產(chǎn)出來的。單例模式的單例模式,所謂單例模式就是一個類只能創(chuàng)建一個實例化。 簡單工廠模式 工廠模式有一種非常形象的描述,建立對象的類就如一個工廠,而需要被建立的對象就是一個個產(chǎn)品;在工廠中加工產(chǎn)品,使用產(chǎn)品的人,不用在乎產(chǎn)品是如何生產(chǎn)出來...
閱讀 2416·2021-11-25 09:43
閱讀 1195·2021-09-07 10:16
閱讀 2603·2021-08-20 09:38
閱讀 2937·2019-08-30 15:55
閱讀 1449·2019-08-30 13:21
閱讀 884·2019-08-29 15:37
閱讀 1435·2019-08-27 10:56
閱讀 2093·2019-08-26 13:45