Python 實現單例

Python 實現單例


起步

單例模式是一種常用的軟件設計模式,用來確保一個類只會有一個實例存在。

以下是 Python 中實現單例模式的多種方法,環境基於 Python3.6.6。

__new__函數

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)

每次實例化一個對象時,都會先調用 __new__() 創建一個對象,再調用 __init__() 函數初始化數據。因而,在 new 函數中判斷 Singleton類 是否已經實例化過,如果不是,調用父類的 new 函數創建實例;否則返回之前創建的實例。

_instance 作爲類屬性,保證了所有對象的 _instance 都是同一個。

元類 metaclass

class SingletonMetaClass(type):
    _instance = None

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = super().__call__(*args, **kwargs)
        return self._instance

class Singleton(metaclass=SingletonMetaClass):
    pass

# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)

當一個類設置了元類以後,創建對象是通過調用元類中的 __call__() 函數實現。這一點你可以通過對以下代碼做斷點運行來理解:

class SigletonMetaClass(type):
    _instance = None

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)  # 斷點1

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = super().__call__(*args, **kwargs)
        return self._instance  # 斷點2

class Singleton(metaclass=SigletonMetaClass):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)  # 斷點3

因此,用元類實現單例時仍需按照三步驟:1. 攔截 2. 判斷是否已經創建過對象 3. 返回對象。與上個方法相比,區別在於攔截的地點不同。

裝飾器

用裝飾器實現單例,思路與其他一致,改變的同樣是攔截地點與記錄位置。

函數裝飾器

def SingletonDecorator(cls):
    _instance = None

    def get_instance(*args, **kwargs):
        nonlocal _instance
        if _instance is None:
            _instance = cls(*args, **kwargs)
        return _instance
    return get_instance

@SingletonDecorator
class Singleton(object):
    pass

# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)

類裝飾器

class SingletonDecorator(object):
    _instance = None

    def __init__(self, cls):
        self._cls = cls

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = self._cls(*args, **kwargs)
        return self._instance

@SingletonDecorator
class Singleton(object):
    pass

# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)

方法

靜態方法 staticmethod

class Singleton(object):
    _instance = None

    @staticmethod
    def get_instance():
        cls = __class__

        if cls._instance is None:
            cls._instance = super(cls, cls).__new__(cls)
        return cls._instance

# 示例:
a = Singleton.get_instance()
b = Singleton.get_instance()
# id(a) == id(b)

在靜態函數中,既不會傳入 cls 也不會有 self。爲了在靜態函數中使用 cls 的同時,避免硬編碼,可使用內置變量 __class__。在一個類的作用域中,__class__ 等於類對象,即:__class__ == Singleton

類方法 classsmethod

class Singleton(object):
    _instance = None

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# 示例:
a = Singleton.get_instance()
b = Singleton.get_instance()
# id(a) == id(b)

與靜態函數類似。

名字覆蓋

class Singleton(object):
    def __call__(self):
        return self

Singleton = Singleton()

# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)

實例名覆蓋類名後,執行 Singleton() 就是在調用 __call__() 函數,總是返回自身。

屬性共享

class Singleton(object):
    _state = {}

    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls)
        obj.__dict__ = cls._state
        return obj

    def __init__(self, name):
        self.name = name

# 示例:
a = Singleton()
b = Singleton()
# id(a) != id(b)

實例對象的私有屬性存放在 __dict__ 中。因此,將所有對象指向同一個屬性 Singleton._state,即便它們的 id值 不同,由於共享屬性仍實現了單例效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章