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中使用subprocess庫(kù)創(chuàng)建附加進(jìn)程
這篇文章主要介紹了subprocess庫(kù):Python中創(chuàng)建附加進(jìn)程的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05Python模擬簡(jiǎn)單電梯調(diào)度算法示例
這篇文章主要介紹了Python模擬簡(jiǎn)單電梯調(diào)度算法,涉及Python線程、隊(duì)列、時(shí)間延遲等相關(guān)操作技巧,需要的朋友可以參考下2018-08-08python flask解析json數(shù)據(jù)不完整的解決方法
這篇文章主要介紹了python flask解析json數(shù)據(jù)不完整的解決方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05Python+PyQt5實(shí)現(xiàn)網(wǎng)口功能測(cè)試詳解
這篇文章主要為大家詳細(xì)介紹了Python+PyQt5實(shí)現(xiàn)網(wǎng)口功能測(cè)試的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02PyTorch實(shí)現(xiàn)FedProx聯(lián)邦學(xué)習(xí)算法
這篇文章主要為大家介紹了PyTorch實(shí)現(xiàn)FedProx的聯(lián)邦學(xué)習(xí)算法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05python實(shí)現(xiàn)代理服務(wù)功能實(shí)例
本文分析了代理服務(wù)的原理,并使用PYTHON實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的代理服務(wù)功能供大家參考使用2013-11-11python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測(cè)平臺(tái)
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)Keras搭建RFBnet目標(biāo)檢測(cè)平臺(tái),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05python 基于selectors庫(kù)實(shí)現(xiàn)文件上傳與下載
這篇文章主要介紹了python 基于selectors庫(kù)實(shí)現(xiàn)文件上傳與下載的示例代碼,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2020-12-12