目的: 單個進(jìn)程中只存在一個類的實(shí)例规伐,從而可以實(shí)現(xiàn)數(shù)據(jù)的共享虽惭,節(jié)省系統(tǒng)開銷枚荣,防止io阻塞等等
如何解決:判斷系統(tǒng)是否已有這個單例,如果有則返回,如果沒有就創(chuàng)建
但是在多進(jìn)程的應(yīng)用中灵再,單例模式就實(shí)現(xiàn)不了了,例如一些web應(yīng)用奴艾,django擦俐,這些,因?yàn)闀佣鄺l進(jìn)程來監(jiān)聽http請求握侧,這樣的會通過單例模式是實(shí)現(xiàn)不了數(shù)據(jù)共享的,也就是實(shí)現(xiàn)不了單例模式的目的了嘿期,這時需要用進(jìn)程間通信方法來實(shí)現(xiàn)數(shù)據(jù)共享品擎,當(dāng)然也可以嘗試使用redis這些數(shù)據(jù)庫實(shí)現(xiàn)數(shù)據(jù)共享,因?yàn)樗鼈兊淖x取數(shù)據(jù)較快备徐。
普通模式
class A(object):
def __init__(self,name,male):
self.name = name
self.name = male
#實(shí)例化多個對象
obj1 = A('ben','boy')
obj2 = A('min','girl')
obj3 = A('miao','boy')
##打印內(nèi)存地址萄传,可以看到內(nèi)存地址都是不一樣的
print id(obj1),id(obj2),id(obj3)
單例模式有三種方法:
第一種方法:裝飾器方法
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances: # 如果類沒有在 字典中
instances[cls] = cls(*args, **kwargs) # { 類名: 實(shí)例對象 }
return instances[cls]
return wrapper
@singleton
class Foo(object):
def __init__(self,name):
self.name = name
foo1 = Foo('a')
foo2 = Foo('b')
print id(foo1) 2794874519960
print id(foo2) 2794874519960
第二種方法 使用基類 注意此種方法不接受參數(shù)
class DiJi:
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance'):
cls._instance = super(DiJi, cls).__new__(cls, *args, **kwargs)
return cls._instance
class Foo(DiJi):
pass
foo1 = Foo()
foo2 = Foo()
print id(foo1)
print id(foo2)
第三種方法: 使用元類
元類: 用于創(chuàng)建類的類,類對象創(chuàng)建實(shí)例對象時一定會調(diào)用
__call__
方法,因此在調(diào)用__call__
時候保證始終創(chuàng)建一個實(shí)例即可,type是python中的一個元類
class Singleton(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class Foo(metaclass = Singleton): 不可修改
def __init__(self,name):
self.name = name
foo1 = Foo('s')
foo2 = Foo('a')
print(id(foo1))
print(id(foo2))
print (foo1 is foo2)
結(jié)果
3128008950336
3128008950336
True
第四種方法: 使用模塊
模塊是天然的單例模式,因?yàn)槟K在第一次導(dǎo)入時,會生成 .pyc 文件蜜猾,當(dāng)?shù)诙螌?dǎo)入時秀菱,就會直接加載 .pyc 文件,而不會再次執(zhí)行模塊代碼蹭睡。因此衍菱,我們只需把相關(guān)的函數(shù)和數(shù)據(jù)定義在一個模塊中,就可以獲得一個單例對象了肩豁。
ce.py 在這個文件中
class Singleton(object):
def __init__(self,name):
self.name = name
在另一個文件中
import ce2
print(id(ce2.Singleton('a')))
print(id(ce2.Singleton('b')))
print(id(ce2.Singleton('a')) == id(ce2.Singleton('b')))
結(jié)果
1912694489664
1912694489664
True
多線程中的單例模式
進(jìn)程: 系統(tǒng)進(jìn)行資源分配和調(diào)度的最小單位
線程: CPU調(diào)度和分派的最小單位
協(xié)程: 用戶控制的輕量級線程
加鎖脊串!未加鎖部分并發(fā)執(zhí)行,加鎖部分串行執(zhí)行,速度降低,但是保證了數(shù)據(jù)安全
import time
import os
import threading
class Singleton(object):
_instance_lock = threading.Lock() 線程鎖
def __init__(self,xxx):
self.xxx = xxx
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"): 返回對象是否具有具有給定名稱的屬性。這是通過調(diào)用getattr(obj清钥,name)
with Singleton._instance_lock: 校驗(yàn)是否有線程鎖
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
print(arg)
obj = Singleton.instance(arg)
print('子線程>>',obj)
# print('父進(jìn)程PID',os.getppid())
# print('當(dāng)前進(jìn)程PID:', os.getpid())
for i in range(10):
t = threading.Thread(target=task,args=[i,]).start() # 開啟子線程
time.sleep(5)
obj = Singleton.instance() '類調(diào)用類方法 和 對象調(diào)用實(shí)例方法相同 cls和self不用傳參,因?yàn)閏ls和self就代指的是類和對象,如果instance不是類方法,那就必須傳遞一個位置參數(shù) cls'
print('主進(jìn)程',obj)
結(jié)果
數(shù)字就不列舉了
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
子線程>> <__main__.Singleton object at 0x000001737DDCB518>
主進(jìn)程 <__main__.Singleton object at 0x000001737DDCB518>