[TOC]
參考:
http://www.cnblogs.com/yuanchenqi/articles/8323452.html#_label1
使用文件(模塊導(dǎo)入)
使用 new(類的方式)
使用元類(metaclass)
使用裝飾器(decorator)
在哪里用得到單例:
- 數(shù)據(jù)庫連接池
單例模式:
文件導(dǎo)入方式實(shí)現(xiàn)
利用文件導(dǎo)入懂鸵,那么被導(dǎo)入文件只加載一次的特性,實(shí)現(xiàn)單例模式。
#s1.py
print('s111111111111111111')
#s2.py
print('s22222222222222222222222')
#run.py
import s1
import s2
import s1
import s2
實(shí)現(xiàn)多次導(dǎo)入
import s1
import s2
import s1
import s2
# 如果要想實(shí)現(xiàn)導(dǎo)入多次加載
import importlib
importlib.reload(s1)
importlib.reload(s2)
導(dǎo)入類的實(shí)例,被導(dǎo)入的文件中的類只實(shí)例化一次,而不會(huì)因?yàn)樵谄渌募虮疚募?nèi)多次導(dǎo)入而生成多個(gè)實(shí)例。在連接數(shù)據(jù)庫時(shí),這個(gè)很重要
db.py
class Foo(object):
def __init__(self):
self.conn = "連接數(shù)據(jù)庫"
def get(self):
print(self.conn)
obj = Foo()
# import redis
# obj = redis.Redis(host="10.0.0.61",
# port=6379
# )
view.py
import t_db
print(t_db.obj)
run.py
import t_db
import t_view
print(t_db.obj)
[圖片上傳失敗...(image-e27196-1521871654703)]
類方式
普通實(shí)現(xiàn)
# 1\. 初步實(shí)現(xiàn)
# class Foo(object):
#
# instance = None #靜態(tài)變量跪削,實(shí)例共享
#
# def __init__(self):
# # 因?yàn)槭菃卫瑳]有傳參的必要迂求,因?yàn)槎家缘谝粋€(gè)為準(zhǔn)
# self.a = 1
# self.b = 2
#
# @classmethod
# def get_instance(cls,*args,**kwargs):
# if not getattr(Foo,'instance'): #這個(gè)類中是否有instance碾盐,沒有則創(chuàng)建后返回,有則直接返回
# obj = cls(*args,**kwargs)
# cls.instance = obj
# return cls.instance
#
# obj1 = print(Foo.get_instance())
# obj2 = print(Foo.get_instance())
#
# # 返回
# # <__main__.Foo object at 0x00000000011AAF28>
# # <__main__.Foo object at 0x00000000011AAF28>
# 缺點(diǎn):
# 1.可以實(shí)現(xiàn)單位揩局,但線程不安全
# 2.改變了實(shí)例化的形式(不是Foo())
# 2 最終實(shí)現(xiàn)
import threading
class Foo(object):
lock = threading.Lock() # 實(shí)例化一個(gè)鎖
instance = None #靜態(tài)變量毫玖,實(shí)例共享
def __init__(self):
# 因?yàn)槭菃卫瑳]有傳參的必要凌盯,因?yàn)槎家缘谝粋€(gè)為準(zhǔn)
self.a = 1
self.b = 2
import time
time.sleep(2)
@classmethod
def get_instance(cls,*args,**kwargs):
if not getattr(Foo, 'instance'):
# 每一次線程實(shí)例化時(shí)付枫,都要上鎖再釋放,試想如果實(shí)例化時(shí)不是seleep不是2秒驰怎,而是10秒或更多阐滩,線程進(jìn)來創(chuàng)建實(shí)例,第二次程序中已以有一instance砸西,其實(shí)沒有必要上鎖了叶眉,但還是會(huì)進(jìn)行加鎖處理址儒,所以判斷一下
with cls.lock: # 相當(dāng)于上鎖芹枷,使用完自己relese
if not getattr(Foo,'instance'): #這個(gè)類中是否有instance,沒有則創(chuàng)建后返回莲趣,有則直接返回
obj = cls(*args,**kwargs)
cls.instance = obj
return cls.instance
return cls.instance
# 使用多線程執(zhí)行實(shí)例動(dòng)作鸳慈,看是否有還是單例
def task():
obj = Foo.get_instance()
print(obj)
import threading
for i in range(5):
t = threading.Thread(target=task,)
t.start()
1. 當(dāng)使用多線程進(jìn)行實(shí)例化時(shí),init里如果不加sleep喧伞,那么程序執(zhí)行太快走芋,第二個(gè)線程進(jìn)入程序已經(jīng)有值,所以顯示實(shí)例id還是一樣的
但init里如果加sleep潘鲫,那么第二次進(jìn)入程序翁逞,類中沒有instance,所以會(huì)再創(chuàng)建返回溉仑,這樣的話 就不是單例了挖函,這就是所謂的線程安全
2. 為了解決線程安全,可以使用加鎖解決,但是使程序變成了串行浊竟,并且每一次線程實(shí)例化時(shí)怨喘,都要上鎖再釋放津畸,試想如果實(shí)例化時(shí)不是seleep不是2秒,而是10秒或更多必怜,第一個(gè)線程進(jìn)來創(chuàng)建實(shí)例肉拓,第二個(gè)線程再進(jìn)入程序中已以有一instance,其實(shí)沒有必要上鎖了梳庆,但還是會(huì)進(jìn)行加鎖處理暖途,所以判斷一下
new方法實(shí)現(xiàn)
使用new方法實(shí)現(xiàn)的好處是不會(huì)改變類實(shí)例化的方式還是obj = Foo()
# 使用__new__ 實(shí)現(xiàn)
import threading
import time
class Foo(object):
instance = None
lock = threading.Lock()
def __init__(self):
self.a1 = 1
self.a2 = 2
time.sleep(2)
@classmethod #一定要是類方法
def __new__(cls, *args,**kwargs):
if not cls.instance:
with cls.lock:
if not cls.instance:
obj = super(Foo,cls).__new__(*args, **kwargs) # obj = Foo() 即實(shí)例化Foo
cls.instance = obj
return cls.instance
return cls.instance
def task():
obj = Foo()
print(obj)
import threading
for i in range(5):
t = threading.Thread(target=task,)
t.start()
metaclass形式實(shí)現(xiàn)
metaclass方式的實(shí)現(xiàn)原理
[圖片上傳失敗...(image-1b1b5e-1521871654703)]
import threading
lock = threading.Lock()
class Singleton(type):
# print(123) # 在class創(chuàng)建Foo類時(shí)就會(huì)執(zhí)行Mytype
def __call__(self, *args, **kwargs):
if not hasattr(self,'instance'):
with lock:
# print(self) #<class '__main__.Foo'> ,self 就是Foo
if not hasattr(self,'instance'):
obj = self.__new__(self, *args, **kwargs) # new實(shí)例化一個(gè)對(duì)象,即如果沒instance膏执,那么創(chuàng)建一個(gè)
obj.__init__( *args, **kwargs) # 對(duì)象.init 即構(gòu)造這個(gè)實(shí)例
setattr(self,'instance',obj) # 在這個(gè)實(shí)例中設(shè)置一個(gè)靜態(tài)變量instance
return getattr(self,'instance')
return getattr(self, 'instance')
class Foo(object,metaclass=Singleton):
def __init__(self):
self.name = 'xxx'
def task():
obj = Foo()
print(obj)
import threading
for i in range(5):
t = threading.Thread(target=task,)
t.start()
[圖片上傳失敗...(image-2bdda4-1521871654702)]
1. obj1 = Foo() 實(shí)例化類
2. Foo(),加了括號(hào)丧肴,執(zhí)行父類的__call__
方法
3. 在這個(gè)方法中判斷如果沒有instance
4. 那么創(chuàng)建這個(gè)實(shí)例
5. 使用實(shí)例的init方法構(gòu)造這個(gè)實(shí)例
7. 把實(shí)例賦值給這個(gè)實(shí)例的instance
8. return
9. obj2 = Foo 第二個(gè)實(shí)例化類
10. 如果有instance,已以有了
11. 直接返回
使用metaclass形式的好處是胧后,Mytype類不用變芋浮,如果給讓哪個(gè)類變成單例,那么使他metaclass=Mytype就好了壳快。