首頁  >  文章  >  後端開發  >  Python建立單例模式的5種常用方法

Python建立單例模式的5種常用方法

高洛峰
高洛峰原創
2016-10-18 09:31:521100瀏覽

所謂單例,是指一個類別的實例從頭到尾只能被創建一次。

方法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._inst
if __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

類別屬性_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

結果:

0

1

可以為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 False
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]

   

結果:

2

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._instance
if __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 NameError
import sys
sys.modules[__name__]=_singleton()

   

python並不會對sys.modules進行檢查以確保他們是模組對象,我們利用這一點將模組綁定向一個類別對象,而且以後都會綁定向同一個對象了。

將程式碼存放在single.py中:

>>> import single
>>> single.a=1
>>> single.a=2

   

ConstError

>>> del

class singleton(object):
    pass
singleton=singleton()


將名字singleton綁定到實例上,singleton就是它自己類別的唯一物件了。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn