Python單例模式


所謂單例,是指一個類的實例從始至終只能被創建一次。

方法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
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 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

所有的實例都能當一個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 single.a
ConstError

方法5

最簡單的方法:

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM