Python設(shè)計(jì)模式中單例模式的實(shí)現(xiàn)及在Tornado中的應(yīng)用
單例模式的實(shí)現(xiàn)方式
將類實(shí)例綁定到類變量上
class Singleton(object):
_instance = None
def __new__(cls, *args):
if not isinstance(cls._instance, cls):
cls._instance = super(Singleton, cls).__new__(cls, *args)
return cls._instance
但是子類在繼承后可以重寫(xiě)__new__以失去單例特性
class D(Singleton):
def __new__(cls, *args):
return super(D, cls).__new__(cls, *args)
使用裝飾器實(shí)現(xiàn)
def singleton(_cls):
inst = {}
def getinstance(*args, **kwargs):
if _cls not in inst:
inst[_cls] = _cls(*args, **kwargs)
return inst[_cls]
return getinstance
@singleton
class MyClass(object):
pass
問(wèn)題是這樣裝飾以后返回的不是類而是函數(shù),當(dāng)然你可以singleton里定義一個(gè)類來(lái)解決問(wèn)題,但這樣就顯得很麻煩了
使用__metaclass__,這個(gè)方式最推薦
class Singleton(type):
_inst = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._inst:
cls._inst[cls] = super(Singleton, cls).__call__(*args)
return cls._inst[cls]
class MyClass(object):
__metaclass__ = Singleton
Tornado中的單例模式運(yùn)用
來(lái)看看tornado.IOLoop中的單例模式:
class IOLoop(object):
@staticmethod
def instance():
"""Returns a global `IOLoop` instance.
Most applications have a single, global `IOLoop` running on the
main thread. Use this method to get this instance from
another thread. To get the current thread's `IOLoop`, use `current()`.
"""
if not hasattr(IOLoop, "_instance"):
with IOLoop._instance_lock:
if not hasattr(IOLoop, "_instance"):
# New instance after double check
IOLoop._instance = IOLoop()
return IOLoop._instance
為什么這里要double check?來(lái)看個(gè)這里面簡(jiǎn)單的單例模式,先來(lái)看看代碼:
class Singleton(object):
@staticmathod
def instance():
if not hasattr(Singleton, '_instance'):
Singleton._instance = Singleton()
return Singleton._instance
在 Python 里,可以在真正的構(gòu)造函數(shù)__new__里做文章:
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
這種情況看似還不錯(cuò),但是不能保證在多線程的環(huán)境下仍然好用,看圖:

出現(xiàn)了多線程之后,這明顯就是行不通的。
1.上鎖使線程同步
上鎖后的代碼:
import threading
class Singleton(object):
_instance_lock = threading.Lock()
@staticmethod
def instance():
with Singleton._instance_lock:
if not hasattr(Singleton, '_instance'):
Singleton._instance = Singleton()
return Singleton._instance
這里確實(shí)是解決了多線程的情況,但是我們只有實(shí)例化的時(shí)候需要上鎖,其它時(shí)候Singleton._instance已經(jīng)存在了,不需要鎖了,但是這時(shí)候其它要獲得Singleton實(shí)例的線程還是必須等待,鎖的存在明顯降低了效率,有性能損耗。
2.全局變量
在 Java/C++ 這些語(yǔ)言里還可以利用全局變量的方式解決上面那種加鎖(同步)帶來(lái)的問(wèn)題:
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
在 Python 里就是這樣了:
class Singleton(object):
@staticmethod
def instance():
return _g_singleton
_g_singleton = Singleton()
# def get_instance():
# return _g_singleton
但是如果這個(gè)類所占的資源較多的話,還沒(méi)有用這個(gè)實(shí)例就已經(jīng)存在了,是非常不劃算的,Python 代碼也略顯丑陋……
所以出現(xiàn)了像tornado.IOLoop.instance()那樣的double check的單例模式了。在多線程的情況下,既沒(méi)有同步(加鎖)帶來(lái)的性能下降,也沒(méi)有全局變量直接實(shí)例化帶來(lái)的資源浪費(fèi)。
3.裝飾器
如果使用裝飾器,那么將會(huì)是這樣:
import functools
def singleton(cls):
''' Use class as singleton. '''
cls.__new_original__ = cls.__new__
@functools.wraps(cls.__new__)
def singleton_new(cls, *args, **kw):
it = cls.__dict__.get('__it__')
if it is not None:
return it
cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
it.__init_original__(*args, **kw)
return it
cls.__new__ = singleton_new
cls.__init_original__ = cls.__init__
cls.__init__ = object.__init__
return cls
#
# Sample use:
#
@singleton
class Foo:
def __new__(cls):
cls.x = 10
return object.__new__(cls)
def __init__(self):
assert self.x == 10
self.x = 15
assert Foo().x == 15
Foo().x = 20
assert Foo().x == 20
def singleton(cls): instance = cls() instance.__call__ = lambda: instance return instance # # Sample use # @singleton class Highlander: x = 100 # Of course you can have any attributes or methods you like. Highlander() is Highlander() is Highlander #=> True id(Highlander()) == id(Highlander) #=> True Highlander().x == Highlander.x == 100 #=> True Highlander.x = 50 Highlander().x == Highlander.x == 50 #=> True
相關(guān)文章
python中partial()基礎(chǔ)用法說(shuō)明
這篇文章主要給大家介紹了關(guān)于python中partial()基礎(chǔ)用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2018-12-12
如何在pycharm中配置pyqt5設(shè)計(jì)GUI操作教程
這篇文章主要介紹了如何在pycharm中配置pyqt5設(shè)計(jì)GUI的操作教程,有需要的朋友可以借鑒參考下,希望大家可以多多交流,討論相關(guān)問(wèn)題共同提升2021-08-08
django Layui界面點(diǎn)擊彈出對(duì)話框并請(qǐng)求邏輯生成分頁(yè)的動(dòng)態(tài)表格實(shí)例
這篇文章主要介紹了django Layui界面點(diǎn)擊彈出對(duì)話框并請(qǐng)求邏輯生成分頁(yè)的動(dòng)態(tài)表格實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05
如何在django里上傳csv文件并進(jìn)行入庫(kù)處理的方法
這篇文章主要介紹了如何在django里上傳csv文件并進(jìn)行入庫(kù)處理的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01

