一改艇、使用模塊
其實(shí),Python 的模塊就是天然的單例模式研儒,因?yàn)槟K在第一次導(dǎo)入時(shí)豫缨,會(huì)生成 .pyc 文件,當(dāng)?shù)诙螌?dǎo)入時(shí)端朵,就會(huì)直接加載 .pyc 文件好芭,而不會(huì)再次執(zhí)行模塊代碼。因此冲呢,我們只需把相關(guān)的函數(shù)和數(shù)據(jù)定義在一個(gè)模塊中舍败,就可以獲得一個(gè)單例對(duì)象了。如果我們真的想要一個(gè)單例類敬拓,可以考慮這樣做:
# mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
將上面的代碼保存在文件 mysingleton.py 中邻薯,然后這樣使用:
from mysingleton import my_singleton
my_singleton.foo()
二、使用裝飾器(decorator)
個(gè)人喜歡使用這個(gè)
def singleton(cls, *args, **kw):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return _singleton
@singleton
class MyClass(object):
a = 1
def __init__(self, x=0):
self.x = x
one = MyClass()
two = MyClass()
two.a = 3
print one.a
#3
print id(one)
#29660784
print id(two)
#29660784
print one == two
#True
print one is two
#True
one.x = 1
print one.x
#1
print two.x
#1
三恩尾、使用類
class Singleton(object):
def __init__(self):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
import threading
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
一般情況弛说,大家以為這樣就完成了單例模式,但是這樣當(dāng)使用多線程時(shí)會(huì)存在問題翰意。
程序執(zhí)行后木人,打印結(jié)果如下:
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
看起來也沒有問題信柿,那是因?yàn)閳?zhí)行速度過快,如果在init方法中有一些IO操作醒第,就會(huì)發(fā)現(xiàn)問題了渔嚷,下面我們通過time.sleep模擬
我們?cè)谏厦?strong>init方法中加入以下代碼:
def __init__(self):
import time
time.sleep(1)
重新執(zhí)行程序后,結(jié)果如下:
<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
問題出現(xiàn)了稠曼!按照以上方式創(chuàng)建的單例形病,無法支持多線程
解決辦法:加鎖!未加鎖部分并發(fā)執(zhí)行,加鎖部分串行執(zhí)行,速度降低,但是保證了數(shù)據(jù)安全
import time
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):#這個(gè)作用是優(yōu)化判斷霞幅,預(yù)防已經(jīng)實(shí)例化了就不必進(jìn)入那段代碼睡眠
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
完整代碼
打印結(jié)果如下:
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
這種方式實(shí)現(xiàn)的單例模式漠吻,使用時(shí)會(huì)有限制,以后實(shí)例化必須通過 obj = Singleton.instance()
如果用 obj=Singleton()
,這種方式得到的不是單例
四司恳、基于new方法實(shí)現(xiàn)(推薦使用途乃,方便)
通過上面例子,我們可以知道扔傅,當(dāng)我們實(shí)現(xiàn)單例時(shí)耍共,為了保證線程安全需要在內(nèi)部加入鎖。
我們知道猎塞,當(dāng)我們實(shí)例化一個(gè)對(duì)象時(shí)试读,是先執(zhí)行了類的new方法(我們沒寫時(shí),默認(rèn)調(diào)用object.new)荠耽,實(shí)例化對(duì)象钩骇;然后再執(zhí)行類的init方法,對(duì)這個(gè)對(duì)象進(jìn)行初始化骇塘,所有我們可以基于這個(gè)伊履,實(shí)現(xiàn)單例模式韩容。
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
打印結(jié)果如下:
<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
上面的代碼方法使用__new__
的同時(shí)還要通過加鎖才能實(shí)現(xiàn)單例款违,下面還有一個(gè)可以不加鎖來實(shí)現(xiàn),不過要先初化一個(gè)屬性值群凶。
簡化版:
class Singleton(object):
__instance = None
def __init__(self,name,age):
if self.__first_init:
self.name = name
self.age = age
def __new__(cls,*args,**kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton('a',12)
b = Singleton('b',13)
print(a)
print(b)
print(a.name)
print(b.name)
采用這種方式的單例模式插爹,以后實(shí)例化對(duì)象時(shí),和平時(shí)實(shí)例化對(duì)象的方法一樣 obj = Singleton()
五请梢、基于metaclass方式實(shí)現(xiàn)
1.類由type創(chuàng)建赠尾,創(chuàng)建類時(shí),type的__init__
方法自動(dòng)執(zhí)行毅弧,類() 執(zhí)行type的 __call__
方法(類的__new__
方法,類的__init__
方法)
2.對(duì)象由類創(chuàng)建气嫁,創(chuàng)建對(duì)象時(shí),類的__init__
方法自動(dòng)執(zhí)行够坐,對(duì)象()執(zhí)行類的 __call__
方法
import threading
class SingletonType(type):
_instance_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with SingletonType._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance
class Foo(metaclass=SingletonType):
def __init__(self,name):
self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)