1、什么是單例模式:
單例模式即一個類有且僅有一個實例
先看下面一個例子:
可以看到,我調用了兩次Marry實例化,得到的結果id是不同的,說明,兩次創建了兩個不同的Marry實例。
所以如果我們想要讓類有且僅有一個實例,思路就是創建一個實例,后續再創建的時候,先判斷是否已經存在實例了,如果已經存在了,就直接引用之前創建的實例即可。
2、使用python實現單例模式
方法一:這是最好理解的一種方法
使用python模塊--------python模塊就是天然的單例模式。
python模塊在第一次導入時,會生成 .pyc 文件,第二次導入時,會直接加載 .pyc 文件,而不會再次執行模塊代碼。所以我們可以將需要單例模式的類定義在一個模塊中。
新建文件singleton.py
在文件中實現如下方法:
然后在需要使用實例的地方導入實例marry
from singleton import marry
看下面的測試結果,我多次導入了marry,打印id,發現他們是一樣的,說明Marry實例只創建了一次。
方法二:使用__new__來實現
要理解這種方式,首先需要知道,創建一個類的實例的過程,先執行了類的__new__方法(通常我們沒寫,是默認調用了object的__new__方法),實例化對象,然后再調用__init__方法對對象進行初始化。
如上圖,我們在類Marry的__new__方法中,判斷了,如果Marry的實例對象已經存在,則直接返回,反之才創建對象。
但是上面的這種方式會有一點問題,就是在多線程中會出現問題,如下:
方法三:使用裝飾器實現單例模式
方法四:使用元類
首先需要理解什么是元類?下面來簡單介紹一下:
元類就是可以創建類的類
我們正常創建一個類的時候是這樣的:
class Marry(Person):
pass
在創建Marry類的時候,python做了如下事情:
1)在Marry中查找是否有__metaclass__這個屬性,如果有,那么python會在內存中通過__metaclass__創建一個名為Marry的類對象
2)Marry中沒有找到__metaclass__屬性,則回去她的父類Person中查找,並嘗試用__metaclass__指定的方法創建一個Marry類對象
3)如果任何一個父類中都沒有__metaclass__屬性,python會去模塊中搜索是否有__metaclass__的指定。
4)如果以上都沒有,那么python會使用元類type來創建Marry
我們要使用元類來實現單例模式,就是要自定義一個元類。
自定義元類的目的,就是攔截類的創建,修改一些特性,然后再返回該類。聽起來是不是和裝飾器很相似?
下面言歸正傳,看看通過元類來實現單例模式的代碼:
1 class SingletonType(type): 2 def __call__(cls,*args, **kwargs):# 這里的cls即是Marry類 3 if not hasattr(cls, "_instance"): 4 cls._instance = super(SingletonType, self).__call__(*args, **kwargs)#type的__call__方法會調用Marry的__new__方法創建對象,然后調用__init__初始化對象 5 return cls._instance 6 7 8 class Marry(metaclass=SingletonType):#指定Marry的type為SingletonType 9 def __init__(self): 10 self.name = "marry" 11 self.age = 25 12 13 marry1 = Marry() 14 marry2 = Marry()
上面通過元類的方式實現了單例模式,創建了兩個Marry實例,通過打印marry1和marry2的id,可以看到,他們的id是一樣的,說明他們是同一個實例。
簡單說一下上面元類實現單例模式的過程:
1)類由type創建,創建類時,type的__init__會自動執行。類()執行type的__call__方法(即類的__new__方法和類的__init__方法)
2)對象由類創建,創建對象時,類的__init__方法自動執行,對象()執行類的__call__方法
再看上面的例子:
1)創建類Marry時,指定了元類為SingletonType(metaclass=SingletonType),此時會自動執行元類的__init__方法,class SingletonType中我們沒有寫__init__,但是它繼承了type,所以它會自動執行type的__init__方法。
2)實例化Marry(marry1=Marry())時,會去執行元類的__call__方法,在SingletonType的__call__方法中,我們判斷了cls是否已經包含了實例,如果已經包含了,直接返回類的實例,如果沒有包含,我們會調用type的__call__方法去創建實例。
3)type的__call__方法中會調用cls的__new__方法去創建對象和__init__方法去初始化對象。
4)如果我們給class Marry中加上__call__(cls, *args, **kwargs)方法,我們在調用實例marry1()的時候,會去調用Marry的__call__方法。
參考:https://www.cnblogs.com/huchong/p/8244279.html#navigator