定義
他提供了一個機制:確保一個類只有一個實例對象。
例如日誌記錄,數據庫操作,打印機後臺處理程序等。
實現
最簡單的實現方法是使構造函數私有化,並創建一個靜態方法來完成對象的初始化。然後對象將在第一次調用時創建,之後這個類將返回同一個對象。
注意:
- 類只創建一個對象
- 爲對象提供一個訪問點,使程序可以全局訪問該對象
- 控制共享資源的並行訪問
餓漢式單例
當類初始化的時候,就創建這個實例對象,以後永遠返回同一個實例對象。
class Singleton1(object):
# 通過覆蓋__new__方法來控制對象的創建。
def __new__(cls, *args, **kwargs):
# hasattr用於查看對象cls是否有instance屬性,該屬性作用是檢測該類是否已經生成了一個對象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton1, cls).__new__(cls)
return cls.instance
s = Singleton1()
s1 = Singleton1()
print(s)
print(s1)
'''結果:
<__main__.Singleton1 object at 0x102c3b198>
<__main__.Singleton1 object at 0x102c3b198>'''
懶漢式單例
初始化類的時候不創建對象,第一次調用才創建。這個時候就要注意線程安全性了。
Python內置有一個全局鎖會保證只進入一個線程調用類的方法,所以沒有這個問題,但如果其他語言實現就要考慮這個問題,比如java就應該用synchronized鎖住代碼塊確保線程安全。這一問題也是造成Python的多線程不是真正的多線程的原因。
class Singleton2(object):
__instance = None
# 初始化時,如果存在對象,就直接返回這個對象,不存在就不管,也不new它
def __init__(self):
if Singleton2.__instance:
self.get_instance()
# 實際的對象創建發生在調用get_instance的時候
@classmethod
def get_instance(cls):
if not cls.__instance:
cls.__instance = Singleton2()
return cls.__instance
s = Singleton2()
print(s.get_instance())
s1 = Singleton2()
print(s1.get_instance())
'''結果:
<__main__.Singleton2 object at 0x110062240>
<__main__.Singleton2 object at 0x110062240>'''
使用元類實現單例
元類MetaSingleton生成的實例是Logger類,關於__call__方法的調用,因爲Logger是MetaSingleton的一個實例,所以Logger()實際上就調用了Singleton的__call__方法,所以在這一層面上控制了Logger對象的單例。
如果不能理解,看下面模塊:
1、python類裏的__call__()魔法方法能夠讓類的實例對象像函數一樣被調用。意思就是:A類創建對象a:
a = new A()
,a是一個對象,不能調用,但如果A類有call方法,他就能通過a()
執行A類的call方法。
2、Python一切皆對象,包括你寫的類,你寫的Python文件等。所以既然是對象,就有他的類。類的類就叫元類,元類也有類,元類的類是type類,type是Python的內建元類。
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton,
cls).__call__(*args, **kwargs)
return cls._instances
class Logger(metaclass=MetaSingleton):
pass
logger1 = Logger()
logger2 = Logger()
print(logger1)
print(logger2)
'''結果:
{<class '__main__.Logger'>: <__main__.Logger object at 0x10357be10>}
{<class '__main__.Logger'>: <__main__.Logger object at 0x10357be10>}'''
單例模式優缺點
- 單例模式優點很明顯:只提供一個實例化的對象。
- 單例模式具有全局訪問權限,全局變量可能在某處已經被修改,但是開發人員仍然認爲他們沒有發生變化。
- 可能會對同一個對象創建多個引用。
- 所有依賴於全局變量的類都會由於一個類的改變而緊密偶合爲全局數據,從而可能在無意中影響另一個類。