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值 不同,由於共享屬性仍實現了單例效果。