1. 單例模式html
單例模式(Singleton Pattern)是一種經常使用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你但願在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。python
好比,某個服務器程序的配置信息存放在一個文件中,客戶端經過一個 AppConfig 的類來讀取配置文件的信息。若是在程序運行期間,有不少地方都須要使用配置文件的內容,也就是說,不少地方都須要建立 AppConfig 對象的實例,這就致使系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤爲是在配置文件內容不少的狀況下。事實上,相似 AppConfig 這樣的類,咱們但願在程序運行期間只存在一個實例對象。設計模式
在 Python 中,咱們能夠用多種方法來實現單例模式安全
1.1 使用模塊(不推薦)服務器
Python 的模塊就是自然的單例模式,由於模塊在第一次導入時,會生成 .pyc
文件,當第二次導入時,就會直接加載 .pyc
文件,而不會再次執行模塊代碼。所以,咱們只需把相關的函數和數據定義在一個模塊中,就能夠得到一個單例對象了。若是咱們真的想要一個單例類,能夠考慮這樣作:多線程
mysingleton.pyapp
class Singleton(object): def foo(self): pass singleton = Singleton()
將上面的代碼保存在文件 mysingleton.py
中,要使用時,直接在其餘文件中導入此文件中的對象,這個對象便是單例模式的對象函數
from mysingleton import singleton
對這種使用模塊來實現單例模式的方法,只能在一些特定場景中使用(好比初始化一個config),由於很差修改和維護。好比若是我想在代碼運行過程當中,根據實時的參數生成一個單例,這種方法就很差用了。post
1.2 使用裝飾器(不考慮多線程影響時可使用)優化
>>> def Singleton(cls): _instance = {} def _singleton(*args,**kw): if cls not in _instance: _instance[cls] = cls(*args,**kw) return _instance[cls] return _singleton >>> @Singleton class A(object): a = 1 def __init__(self,x = 0): self.x = x >>> a1 = A(2) >>> a1 <__main__.A object at 0x000000893F3B0630> >>> a1.x 2 >>> a2 = A(3) >>> a2 <__main__.A object at 0x000000893F3B0630> >>> a2.x 2
有幾個知識點值得注意:
<1> 裝飾器不只能夠裝飾函數,也能夠用來裝飾類
<2> 咱們經過查看生成的對象a1和a2,發現他們的內存地址都是同樣的,是"0x000000893F3B0630",所以能夠看出來是同一個對象,即驗證了咱們的單例模式是成功的
<3> 執行a2 = A(3),咱們能夠看到a2.x = 2,而不是3。 這是由於正常狀況下語句a2 = A(3)會調用__init__函數,將3傳給x。可是因爲咱們是單例模式,a2 = A(3)並不會生成新的對象,而是將以前生成的a1的對象返回給了a2,由於__init__函數只有在生成新的對象時纔會執行,因此a2.x = 2
可是這樣的實現方式是有漏洞的,當在多進程運行狀況下,一旦__init__函數中有些耗時比較長的操做,會發生下面的狀況:進程a和進程b同時執行,若是此時實例並無被生成,a和b會同時嘗試去生成實例,而且因爲__init__耗時較長,a和b在生成實例時,都沒有現成的實例,就會形成a和b生成了不一樣的實例,咱們的單例模式就失敗了。
下面是例子
>>> def Singleton(cls):
@functools.wrap(cls) _instance = {} def wrapper(*args,**kw): if cls not in _instance: _instance[cls] = cls(*args,**kw) return _instance[cls] return wrapper >>> import time >>> @Singleton class A(object): a = 1 def __init__(self,x = 0): time.sleep(2) self.x = x >>> import threading >>> def task(arg): obj = A(arg) print(obj, obj.x) >>> for i in range(10): t = threading.Thread(target = task, args=[i,]) t.start() >>> <__main__.A object at 0x000000893F5F8D30><__main__.A object at 0x000000893F5F82B0><__main__.A object at 0x000000893F5F8B38><__main__.A object at 0x000000893FBE4358><__main__.A object at 0x000000893FBE4160><__main__.A object at 0x000000893F5F8F28><__main__.A object at 0x000000893F5F8940><__main__.A object at 0x000000893FBE4550><__main__.A object at 0x000000893FBE4940><__main__.A object at 0x000000893FBE4748> 3026541798
經過這些實例的地址咱們能夠看出來,這也instance是不一樣的實例,咱們的單例模式失敗了
1.3 使用類(基於__new__方法實現)
經過上面例子,咱們能夠知道,當咱們實現單例時,爲了保證線程安全須要在內部加入鎖
咱們知道,當咱們實例化一個對象時,是先執行了類的__new__方法(咱們沒寫時,默認調用object.__new__),實例化對象;而後再執行類的__init__方法,對這個對象進行初始化,全部咱們能夠基於這個,實現單例模式
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()
值得注意的是類的實現中有兩個判斷:
第一個判斷,是爲了當單例對象生成後,再也不鎖多進程
第二個判斷,是爲了當單例對象還未生成時,確保只有一個對象被生成。(若是沒有這一層判斷,當對象被第一個進程生成後,後續的進程還會生成新的對象)
下面是例子:
只有一個判斷,生成了多個實例。單例失敗。
>>> class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls,*args,**kw): if not hasattr(Singleton,"_instance"): with Singleton._instance_lock: Singleton._instance = Singleton(*args,**kw) 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() >>> <__main__.Singleton object at 0x000000893F5F8B38> <__main__.Singleton object at 0x000000893FBE44A8> <__main__.Singleton object at 0x000000893F5F8A90> <__main__.Singleton object at 0x000000893F5F8E10> <__main__.Singleton object at 0x000000893F5F87B8> <__main__.Singleton object at 0x000000893F5F8C18> <__main__.Singleton object at 0x000000893F5F8A90> <__main__.Singleton object at 0x000000893F5F8710> <__main__.Singleton object at 0x000000893F5F8BA8> <__main__.Singleton object at 0x000000893F5F8E10>
有兩個判斷,只生成了一個實例,單例成功
>>> class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls,*args,**kw): if not hasattr(Singleton,"_instance"): with Singleton._instance_lock: if not hasattr(Singleton,"_instance"): Singleton._instance = Singleton(*args,**kw) 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() >>> <__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90><__main__.Singleton object at 0x000000893F5F8A90>
咱們須要特別注意的是,這種實現方法也很差,由於是在單例模式的實現代碼,寫在了類的實現裏。咱們每要實現一個單例模式的類,就要在類的定義裏寫相應的代碼。這樣沒有把設計模式和類的具體實現分離。
1.4 基於元類(metaclass)實現(推薦)
關於元類的知識點,能夠參考我以前寫的博文: 談談Python中元類Metaclass(一):什麼是元類
如下是幾個關鍵的知識點:
>>> import time >>> 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 time.sleep(1) >>> def task(arg): obj = Foo(arg) print(obj,obj.name) >>> for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() >>> <__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8><__main__.Foo object at 0x000000893F5F87B8> 0000000000
這樣,咱們就完美的實現了單例模式,同時將達成了更優的目的:單例模式這個設計模式與類的定義分開。
參考連接: