Python创建单例模式的5种方法
單例模式(Singleton Pattern)是一種常用的軟件設計模式,是指一個類的實例從始至終只能被創建一次,同時它提供一個靜態的getInstance()工廠方法,讓客戶可以訪問它的唯一實例;為了防止在外部對其實例化,將其構造函數設計為私有;在單例類內部定義了一個Singleton類型的靜態對象,作為外部共享的唯一實例。
主要優點:
1、提供了對唯一實例的受控訪問。
2、由于在系統內存中只存在一個對象,因此可以節約系統資源,對于一些需要頻繁創建和銷毀的對象單例模式無疑可以提高系統的性能。
3、允許可變數目的實例。
主要缺點:
1、由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
2、單例類的職責過重,在一定程度上違背了“單一職責原則”。
3、濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。
適用場景
在以下情況下可以考慮使用單例模式:
-
(1) 系統只需要一個實例對象,如系統要求提供一個唯一的序列號生成器或資源管理器,或者需要考慮資源消耗太大而只允許創建一個對象。
-
(2) 客戶調用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。
實現某個類只有一個實例的途徑:
1,讓一個全局變量使得一個對象被訪問,但是他不能防止外部實例化多個對象。
2,讓類自身保存他的唯一實例,這個類可以保證沒有其他實例可以被創建。
多線程時的單例模式:加鎖-雙重鎖定
餓漢式單例類:在類被加載時就將自己實例化(靜態初始化)。其優點是躲避了多線程訪問的安全性問題,缺點是提前占用系統資源。
懶漢式單例類:在第一次被引用時,才將自己實例化。避免開始時占用系統資源,但是有多線程訪問安全性問題。
方法1:使用__new__方法
如果想使得某個類從始至終最多只有一個實例,使用__new__方法會很簡單。Python中類是通過__new__來創建實例的:
class Singleton(object):def __new__(cls,*args,**kwargs):if not hasattr(cls,'_inst'):cls._inst=super(Singleton,cls).__new__(cls,*args,**kwargs)return cls._inst if __name__=='__main__':class A(Singleton):def __init__(self,s):self.s=s a=A('java') b=A('python')print id(a),a.sprint id(b),b.s結果:
9621235 python 9921235 python通過__new__方法,將類的實例在創建的時候綁定到類屬性_inst上。如果cls._inst為None,說明類還未實例化,實例化并將實例綁定到cls.inst,以后每次實例化的時候都返回第一次實例化創建的實例。注意從Singleton派生子類的時候,不要重載__new_。
方法2:使用裝飾器
我們知道,裝飾器(decorator)可以動態地修改一個類或函數的功能。這里,我們也可以使用裝飾器來裝飾某個類,使其只能生成一個實例,代碼如下:
from functools import wraps def singleton(cls):instances = {}@wraps(cls)def getinstance(*args, **kw):if cls not in instances:instances[cls] = cls(*args, **kw)return instances[cls]return getinstance @singleton class MyClass(object):a = 1在上面,我們定義了一個裝飾器 singleton,它返回了一個內部函數 getinstance,該函數會判斷某個類是否在字典 instances 中,如果不存在,則會將 cls 作為 key,cls(*args, **kw) 作為 value 存到 instances 中,否則,直接返回 instances[cls]。
方法3:使用元類(metaclass)
當你編寫一個類的時候,某種機制會使用類名字,基類元組,類字典來創建一個類對象。新型類中這種機制默認為type,而且這種機制是可編程的,稱為元類__metaclass__ 。
class Singleton(type):def __init__(self,name,bases,class_dict):super(Singleton,self).__init__(name,bases,class_dict)self._instance=Nonedef __call__(self,*args,**kwargs):if self._instance is None:self._instance=super(Singleton,self).__call__(*args,**kwargs)return self._instance if __name__=='__main__':class A(object):__metaclass__=Singleton a=A()b=A()print id(a),id(b)結果:
43645654 43645654id是相同的。
例子中我們構造了一個Singleton元類,并使用__call__方法使其能夠模擬函數的行為。構造類A時,將其元類設為Singleton,那么創建類對象A時,行為發生如下:
A=Singleton(name,bases,class_dict),A其實為Singleton類的一個實例。
創建A的實例時,A()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__(),這樣就將A的所有實例都指向了A的屬性_instance上,這種方法與方法1其實是相同的。
方法4:使用模塊
python中的模塊module在程序中只被加載一次,本身就是單例的。可以直接寫一個模塊,將你需要的方法和屬性,寫在模塊中當做函數和模塊作用域的全局變量即可,根本不需要寫類。
而且還有一些綜合模塊和類的優點的方法:
class _singleton(object):class ConstError(TypeError):passdef __setattr__(self,name,value):if name in self.__dict__:raise self.ConstErrorself.__dict__[name]=valuedef __delattr__(self,name):if name in self.__dict__:raise self.ConstErrorraise NameError import sys sys.modules[__name__]=_singleton()python并不會對sys.modules進行檢查以確保他們是模塊對象,我們利用這一點將模塊綁定向一個類對象,而且以后都會綁定向同一個對象了。
將代碼存放在single.py中:
>>> import single >>> single.a=1 >>> single.a=2 ConstError >>> del single.a ConstError方法5:名字綁定法
最簡單的方法:
class singleton(object):pass singleton=singleton()將名字singleton綁定到實例上,singleton就是它自己類的唯一對象了。
總結
以上是生活随笔為你收集整理的Python创建单例模式的5种方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python3 queue队列模块详解
- 下一篇: Python中is和==的区别