單例模式是設計模式(Design Pattern)中最簡單、最容易理解的一種,維基百科[1]的定義以下:html
單例模式,也叫 單子模式,是一種經常使用的 軟件設計模式。在應用這個模式時,單例對象的 類 "類 (計算機科學)")必須保證只有一個實例存在。許多時候整個系統只須要擁有一個的全局 對象,這樣有利於咱們協調系統總體的行爲。
單例模式的主要優勢是共享資源和減小資源消耗,主要應用於IO或數據庫的線程池,緩存,日誌,對話和需共享數據的資源等,可是在實現狀況中濫用單例模式會帶來不少意想不到的問題,本文重點在於介紹幾種Python實現單例模式的方法,這裏就再也不展開論述了。文中所演示的代碼都會託管在Github上。python
首先,咱們先嚐試用Python內部類(嵌套類)來實現單例模式:git
#coding=utf-8 class Singleton: """單列類 """ class __MyClass: """實際生成實例的類 """ def __init__(self, arg): """初始化並賦值""" self.foo = arg def display(self): """返回實例的id和屬性值""" return (id(self), self.foo) # 類屬性 _instance = None def __init__(self, arg): if not Singleton._instance: Singleton._instance = Singleton.__MyClass(arg) else: Singleton._instance.foo = arg def __getattr__(self, attr): return getattr(self._instance, attr)
注意實際生成實例的類是內部的「__MyClass」類,前面的雙下劃線表明這是一個私有的類,用戶不能再外面直接訪問它。而在"__MyClass"類外封裝了一個「Singleton」類,這個類的任務就是在初始化時保證整個上下文中只有一個實例,實現的方式很簡單。用一個私有屬性__instance_保存當前生成的實例,在初始化時判斷實例是否爲_None_,若是是就用「__MyClass」類生成一個新實例並賦值給__instance_,否就直接返回或調用當前__instance_的實例。最後用"__MyClass"裏的實現的方法測試一下:github
if __name__ == "__main__": """測試""" s1 = Singleton("bar") s2 = Singleton("zoo") print(s1.display()) print(s2.display()) # output >(41706760L, 'zoo') >(41706760L, 'zoo')
如今咱們考慮將inner class拆分出來,由於在Python類實例化時會調用___new___方法[2]來生成實例,因此咱們能夠先繼承「Singleton」類,而後經過重寫基類的___new___方法讓其實現單例模式:數據庫
#coding=utf-8 class Singleton(object): """單例類 """ _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance class MyClass(Singleton): """實際生成實例的類 """ def __init__(self, arg): self.foo = arg def display(self): return (id(self), self.foo)
測試結果:設計模式
if __name__ == "__main__": s1 = MyClass("bar") s2 = MyClass("zoo") print(s1.display()) print(s2.display()) assert s1 is s2 # output >(40882416L, 'zoo') >(40882416L, 'zoo')
第三種就是最多見的用裝飾器來實現單列模式:緩存
#coding=utf-8 def singleton(cls): instances = {} def wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class MyClass: """實際生成實例的類 """ foo = "foo" def display(self): return (id(self)) @singleton class OtherClass: """另外一個類 """ pass
裝飾器的實現過程是將生成的實例都放到一個名爲_instances_的Dict中映射好,這樣每次在類初始化時先檢查_instances_中是否已經包含有例化好的實例,有就直接返回是咧,沒有則調用類初始化一個並賦值給_instances_列表。裝飾器的好處在於用一個Dict列表來管理全部須要實現單例模式的類,更簡便和通用化。代碼的測試結果以下:安全
if __name__ == "__main__": s1 = MyClass() s1.foo = "bar" print(s1.display(), s1.foo) s2 = MyClass() s2.foo = "zoo" print(s2.display(), s2.foo) assert s1 is s2 s3 = OtherClass() s4 = OtherClass() assert s3 is s4
若是但願不單單是經過限制而是在源頭上就建立一個單例類,咱們須要用到元類來實現,元類能夠參考Stackoverflow[3]上的一個解答。簡單的說就是Python中的類也是一種對象,被稱爲類對象。類對象能夠經過元類type來建立,而在此過程當中會調用type的 __call__ 方法。因此咱們只要在type建立類對象的過程當中重寫 __call__ 方法,在其中加入相應的建立單例的邏輯便可實現單例模式,具體代碼實現以下:多線程
#coding=utf-8 class Singleton(type): def __call__(cls, *args, **kwargs): """重寫,實現單例模式""" if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__call__(*args, **kwargs) return cls._instance class MyClass(object): # 指定元類 __metaclass__ = Singleton def display(self): return (id(self))
代碼的測試與前面相似,這裏就再也不累述了。app
最後,須要注意的是單例模式在多線程下可能會出現線程安全的問題,這時候就須要在單例的初始化過程當中加上線程同步鎖來避免,但這樣又會下降總體的性能,具體能夠參考這篇文檔。
[1]維基百科
[2]Python官方文檔
[3]Stackoverflow