所謂單例,是指一個類的執行個體從始至終只能被建立一次。
方法1
如果想使得某個類從始至終最多隻有一個執行個體,使用__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._instif __name__=='__main__': class A(Singleton): def __init__(self,s): self.s=s a=A('apple') b=A('banana') print id(a),a.s print id(b),b.s
結果:
29922256 banana
29922256 banana
通過__new__方法,將類的執行個體在建立的時候綁定到類屬性_inst上。如果cls._inst為None,說明類還未執行個體化,執行個體化並將執行個體綁定到cls._inst,以後每次執行個體化的時候都返回第一次執行個體化建立的執行個體。注意從Singleton派生子類的時候,不要重載__new__。
方法2:
有時候我們並不關心產生的執行個體是否具有同一id,而只關心其狀態和行為方式。我們可以允許許多個執行個體被建立,但所有
class Borg(object): _shared_state={} def __new__(cls,*args,**kwargs): obj=super(Borg,cls).__new__(cls,*args,**kwargs) obj.__dict__=cls._shared_state return obj
將所有執行個體的__dict__指向同一個字典,這樣執行個體就共用相同的方法和屬性。對任何執行個體的名字屬性的設定,無論是在__init__中修改還是直接修改,所有的執行個體都會受到影響。不過執行個體的id是不同的。要保證類執行個體能共用屬性,但不和子類共用,注意使用cls._shared_state,而不是Borg._shared_state。
因為執行個體是不同的id,所以每個執行個體都可以做字典的key:
if __name__=='__main__': class Example(Borg): pass a=Example() b=Example() c=Example() adict={} j=0 for i in a,b,c: adict[i]=j j+=1 for i in a,b,c: print adict[i]
結果:
0
1
2
如果這種行為不是你想要的,可以為Borg類添加__eq__和__hash__方法,使其更接近於單例模式的行為:
class Borg(object): _shared_state={} def __new__(cls,*args,**kwargs): obj=super(Borg,cls).__new__(cls,*args,**kwargs) obj.__dict__=cls._shared_state return obj def __hash__(self): return 1 def __eq__(self,other): try: return self.__dict__ is other.__dict__ except: return Falseif __name__=='__main__': class Example(Borg): pass a=Example() b=Example() c=Example() adict={} j=0 for i in a,b,c: adict[i]=j j+=1 for i in a,b,c: print adict[i]
結果:
2
2
2
所有的執行個體都能當一個key使用了。
方法3
當你編寫一個類的時候,某種機制會使用類名字,基類元組,類字典來建立一個類對象。新型類中這種機制預設為type,而且這種機制是可程式化的,稱為元類__metaclass__ 。
class Singleton(type): def __init__(self,name,bases,class_dict): super(Singleton,self).__init__(name,bases,class_dict) self._instance=None def __call__(self,*args,**kwargs): if self._instance is None: self._instance=super(Singleton,self).__call__(*args,**kwargs) return self._instanceif __name__=='__main__': class A(object): __metaclass__=Singleton a=A() b=A() print id(a),id(b)
結果:
34248016 34248016
id是相同的。
例子中我們構造了一個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): pass def __setattr__(self,name,value): if name in self.__dict__: raise self.ConstError self.__dict__[name]=value def __delattr__(self,name): if name in self.__dict__: raise self.ConstError raise NameErrorimport syssys.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): passsingleton=singleton()
將名字singleton綁定到執行個體上,singleton就是它自己類的唯一對象了。