python 單例模式
Python 單例實現方法
python 實現單例方法有多種,下面我們介紹幾種常用的方法。
1. 使用元類實現單例
- 建立一個 SingleInstance 的元類
- 類指定 metaclass = SingleInstance
# 建立一個 metaclass 類 class SingleInstance(type): """ 類建立物件的時候會呼叫該方法, 返回類物件例項 """ def __call__(cls, *args, **kwargs): instance = getattr(cls, '__instance__', None) if instance is None: instance = super(SingleInstance, cls).__call__(*args, **kwargs) setattr(cls, '__instance__', instance) print("create instance ", instance) else: print("use old instance ", instance) print('got instance for ', cls) return instance """ 測試方法: 1. 每次實驗建立兩個類,確認不同的類之間不會相互影響 2. 每個類建立兩個物件,確認兩個物件是一樣的 """ class TestClass0(metaclass=SingleInstance): def __init__(self): print("init test0 class object") class TestClass1(metaclass=SingleInstance): def __init__(self): print("init test1 class object") # 測試第一個類物件 test00 = TestClass0() test01 = TestClass0() print(test00, test01) print('\n----------------------\n') # 測試第二個類物件 test10 = TestClass1() test11 = TestClass1() print(test10, test11)
init test0 class object create instance<__main__.TestClass0 object at 0x7f5492fe66a0> got instance for<class '__main__.TestClass0'> use old instance<__main__.TestClass0 object at 0x7f5492fe66a0> got instance for<class '__main__.TestClass0'> <__main__.TestClass0 object at 0x7f5492fe66a0> <__main__.TestClass0 object at 0x7f5492fe66a0> ---------------------- init test1 class object create instance<__main__.TestClass1 object at 0x7f5492fe69b0> got instance for<class '__main__.TestClass1'> use old instance<__main__.TestClass1 object at 0x7f5492fe69b0> got instance for<class '__main__.TestClass1'> <__main__.TestClass1 object at 0x7f5492fe69b0> <__main__.TestClass1 object at 0x7f5492fe69b0>
2. 通過類的 new 方法實現
- 第一次建立物件並儲存到類屬性中
- 後續從類屬性中取出物件,不再建立新的物件
class SingleClass(): def __new__(cls, *args, **kwargs): print('new instance for ', cls) if not hasattr(cls, '__instance__'): print('create instance for ', cls) instance = super(SingleClass, cls).__new__(cls, *args, **kwargs) cls.__instance__ = instance return cls.__instance__ class SubClass(SingleClass): pass test00 = SingleClass() test01 = SingleClass() print(test00, test01) print('\n----------------------\n') test10 = SubClass() test11 = SubClass() print(test10, test11)
new instance for<class '__main__.SingleClass'> create instance for<class '__main__.SingleClass'> new instance for<class '__main__.SingleClass'> <__main__.SingleClass object at 0x7f5492fc75f8> <__main__.SingleClass object at 0x7f5492fc75f8> ---------------------- new instance for<class '__main__.SubClass'> new instance for<class '__main__.SubClass'> <__main__.SingleClass object at 0x7f5492fc75f8> <__main__.SingleClass object at 0x7f5492fc75f8>
我們發現這樣實現是有問題的,SubClass 無法建立自己的單例。 因為父類 SingleClass 的類物件資料會被所有子類共享,導致所有 SingleClass 子類無法建立新的物件。
解決方法是我們在 SingleClass 類中使用字典儲存每個子類的例項物件,改進後代碼如下:
class SingleTClass(): __instance__ = {} def __new__(cls, *args, **kwargs): print('new instance for ', cls) if not cls in cls.__instance__: print('create instance for ', cls) instance = super(SingleTClass, cls).__new__(cls, *args, **kwargs) cls.__instance__[cls] = instance return cls.__instance__[cls] class SubTClass(SingleTClass): pass test00 = SingleTClass() test01 = SingleTClass() print(test00, test01) print('\n----------------------\n') test10 = SubTClass() test11 = SubTClass() print(test10, test11)
new instance for<class '__main__.SingleTClass'> create instance for<class '__main__.SingleTClass'> new instance for<class '__main__.SingleTClass'> <__main__.SingleTClass object at 0x7f5492fc7ac8> <__main__.SingleTClass object at 0x7f5492fc7ac8> ---------------------- new instance for<class '__main__.SubTClass'> create instance for<class '__main__.SubTClass'> new instance for<class '__main__.SubTClass'> <__main__.SubTClass object at 0x7f5492fc7438> <__main__.SubTClass object at 0x7f5492fc7438>
3. 通過裝飾器實現單例
- 建立一個 singleclass 的裝飾器
- 建立類的時候使用上述裝飾器
from functools import wraps """ 把類物件變成一個函式,每次呼叫類物件建立例項的時候,實際是呼叫裝飾器返回的函式 """ def singleclass(cls): __instance__ = {} @wraps(cls) def cls_instance(*args, **kwargs): print('new instance for ', cls) if not cls in __instance__: __instance__[cls] = cls(*args, **kwargs) return __instance__[cls] return cls_instance @singleclass class TestD1Class(): pass @singleclass class TestD2Class(): pass test00 = TestD1Class() test01 = TestD1Class() print(test00, test01) print('\n----------------------\n') test10 = TestD2Class() test11 = TestD2Class() print(test10, test11)
new instance for<class '__main__.TestD1Class'> new instance for<class '__main__.TestD1Class'> <__main__.TestD1Class object at 0x7f5492fee518> <__main__.TestD1Class object at 0x7f5492fee518> ---------------------- new instance for<class '__main__.TestD2Class'> new instance for<class '__main__.TestD2Class'> <__main__.TestD2Class object at 0x7f5492fee160> <__main__.TestD2Class object at 0x7f5492fee160>