使用python實現設計模式中的單例模式。單例模式是一種比較經常使用的設計模式,其實現和使用場景斷定都是相對容易的。本文將簡要介紹一下python中實現單例模式的幾種常見方式和原理。一方面能夠加深對python的理解,另外一方面能夠更加深刻的瞭解該模式,以便實際工做中能更加靈活的使用單例設計模式。python
本文將介紹常見的實現單例模式的幾種方式,這裏暫不考慮多線程的狀況。設計模式
爲了準備該篇博文,以前寫了幾篇相關的文章依次完整的介紹了相關的概念,下面會在須要的時候給出連接。多線程
裝飾器做爲python實現單例模式的一種經常使用方法,先簡單瞭解一下其概念。閉包
1.裝飾器app
裝飾器(Decorator)能夠用做對函數以及類進行二次包裹或者封裝,使用方式@wrapper。ide
上面這兩種方式對函數的定義在語法上是等價的。固然對於類也有一樣的用法,類能夠做爲裝飾器也能夠做爲被裝飾對象。惟一的區別就是通過包裹的類可能不在是一個類,而是一個類的對象或者一個函數,這取決於裝飾器返回的值。函數
通過Decorator裝飾的類或者函數本質上已經再也不是原來的類或者函數了。可是,實際上在包裹以後獲得的新對象仍然擁有被包裹對象的特性(這句是否是廢話:-))。學習
在python中咱們常常只須要實現一個裝飾器,而後使用該裝飾器做用於只能有惟一一個實例的類。這樣只須要實現一個這樣的裝飾器,即可以做用於任何一個想要惟一實例的類。線程
2.閉包方式設計
閉包的應用不少,單例模式則是其應用之一。先看代碼:
這個實現單例模式的方式將原來類的定義隱藏在閉包函數中,經過閉包函數及其中引用的自由變量來控制類對象的生成。因爲惟一的實例存放在自由變量中,並且自由變量是沒法直接在腳本層進行訪問的。這種方式很是隱蔽的保護實例不被修改,所以很適合用於單例模式。
這種方式簡單明瞭,很容易實現。可是若是不瞭解閉包實現過程和變量的綁定等概念可能會不明白其實現的過程。建議參考一下個人另外一篇博文:理解python閉包概念。
這裏一個頗有趣的地方是爲何要使用instances = {}這樣一個變量?可不能夠不用字典,使用instance = None?若是singleton做爲裝飾器被多個不一樣的類使用,那麼instance中會存在幾個不一樣的實例麼?
有時間能夠思考一下這幾個問題,答案也能夠在我寫的閉包相關的博文中找到。
3.元類方式
所謂單例模式,即咱們須要控制類實例的生成過程,而且保證全局只可能存在一個惟一的實例。既然須要在建立類的對象過程當中作些什麼,應該很容易想到元類。
這個例子中咱們使用元類Singleton替代默認使用type方式建立類my_cls。能夠將類my_cls看作是元類Singleton的一個對象,當咱們使用my_cls(...)的方式建立類my_cls的對象時,其實是在調用元類Singleton的對象my_cls。
對象能夠以函數的方式被調用,那麼要求類中定義__call__函數。不過此處被調用的是類,所以咱們在元類中定義函數__call__來控制類my_cls對象建立的惟一性。
這種方式的弊端之一就是類惟一的對象被存放在類的一個靜態數據成員中,外部能夠經過class_name._instance的方式修改甚至刪除這個實例(該例中my_cls._instance = None徹底合法)。
4.類做爲裝飾器之__call__方式
不只函數能夠做爲裝飾器,類也能夠做爲裝飾器。
下面簡單的介紹一下使用類做爲裝飾器實現單例模式的另外一種方式。
函數做爲裝飾器返回的是一個函數,函數被調用過程當中其實是間接地調用其內部包裹的被裝飾的對象。
類做爲裝飾器要想達到相同的效果只須要將類的對象返回,而且其對象是能夠調用的。這是上面這個例子表達的一個核心思想。
這種方式寫法不少,也很靈活,其思想基本上就是對被包裹對象的調用實際上調用的是類對象的__call__函數,該函數其實是對被裝飾對象的一次封裝。
5.類自己實現方式
上面的例子中咱們都是使用的裝飾器或者元類的方式間接的經過控制類對象生成的方式來保證對象的惟一性,那麼有沒有辦法直接在類中經過某種方式保證類對象的惟一性?
答案是確定的。參考我以前寫的一篇介紹元類的文章,可知生成對象前會調用函數__new__,若是__new__函數返回被建立的對象,那麼會自動調用類中定義的__init__函數進行對象的初始化操做。
相信讀了上面這句話,應該知道咱們接下來要幹什麼了?沒錯,咱們的目標就是__new__。
在這個例子中,咱們徹底能夠理解爲何只會有一個類的對象會被建立。這種方式的定義決定了類自己只能被建立一個對象。
可是這裏有一點須要注意,那就是無論建立多少MSC的對象,至始至終只會有一個對象,可是若是每次建立的時候傳入的參數都不一樣,也就是__init__函數中參數不一樣,會致使同一個對象被屢次初始化。
這種方式的弊端顯然很明顯,那就是該方法只能做用於單個類的定義。不能像上面的裝飾器和元類,一次實現,能夠處處使用。
那能不能將這個控制類生成過程的結構單獨抽象出來呢?並且有沒有什麼方法能防止同一個對象屢次被__init__初始化。下面咱們看一種能被不一樣的類使用的更加抽象的結構。
6.替換__new__方式
咱們定義的類做爲一個對象,經過替換其部分屬性能夠達到控制類對象生成的目的。
上面咱們經過替換類的__new__函數和__init__函數的方式,保證被Singleton裝飾的類只有一個對象會被原來的__new__和__init__生成和初始化。
這裏必需要替換類的__init__函數,並且該函數應該什麼都不作。緣由在於替換以後的__new__返回惟一的對象後,會自動調用如今的__init__函數。
原來的__init__函數已經在建立惟一一個對象時被調用過。並且只能被調用一次。
這裏返回的並非閉包結構,只是使用裝飾器修改了類的部分屬性,返回的還是傳入的類。可是類的__new__函數引用了Singleton中的local variable _instance。
Cell 對象my_cls.__new__.func_closure[0]中存放的即是類my_cls惟一的實例。
固然咱們能夠將my_cls惟一的對象做爲類的一個靜態數據成員放入cls.__dict__中來替代_instance = {},可是顯然閉包結構更適合。
7.注意事項
文中藉助python語言的類建立對象過程的相關原理,介紹了幾種不一樣的單例模式實現方式。
爲了保留被裝飾對象的一些屬性,可使用@functools.wraps的方式對返回的閉包進行裝飾。
平時建議使用前兩種實現方式,也就是閉包方式和元類方式。其餘狀況多少有點玩弄python語法技巧的一些嫌疑,固然了,做爲學習python來講仍是比較有意義的。 想要學習Python開發的同窗,能夠參考成都Python培訓班提供的學習大綱;