儘管單例設計模式的優劣不是本文的討論重點,本文將探討如何在Python 中以最符合Python 風格的方式實現此模式。在這裡,"最符合 Python 風格" 的意思是遵循"最少驚訝原則"。
def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass
優點:
缺點:
class Singleton(object): _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton, BaseClass): pass
優點:
缺點:
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] # Python2 class MyClass(BaseClass): __metaclass__ = Singleton # Python3 class MyClass(BaseClass, metaclass=Singleton): pass
優點:
缺點:
def singleton(class_): class class_w(class_): _instance = None def __new__(class_, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs) class_w._instance._sealed = False return class_w._instance def __init__(self, *args, **kwargs): if self._sealed: return super(class_w, self).__init__(*args, **kwargs) self._sealed = True class_w.__name__ = class_.__name__ return class_w @singleton class MyClass(BaseClass): pass
優點:
缺點:
單例模組 singleton.py。
優點:
缺點:
我建議使用 方法 2,但最好使用元類別而不是基底類別。以下是一個範例實作:
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Logger(object): __metaclass__ = Singleton
或在Python3 中:
class Logger(metaclass=Singleton): pass
如果希望每次呼叫類別時都執行__init__,請將下列程式碼加入Singleton.__call__中的if 語句中:
def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass
元類是類的類,也就是說,類是其元類的實例。可以透過 type(obj) 找到 Python 中物件的元類別。正常的新的類別是 type 類型。上面的 Logger 將屬於 class 'your_module.Singleton' 類型,就像 Logger 的(唯一)實例將屬於 class 'your_module.Logger' 類型一樣。當使用 Logger() 呼叫 logger 時,Python 首先詢問 Logger 的元類別 Singleton 應該做什麼,允許搶先進行實例建立。這個過程與 Python 透過呼叫 __getattr__ 詢問類別應該做什麼來處理其屬性類似,而你透過執行 myclass.attribute 來引用其屬性。
元類別本質上決定了呼叫類別的含義以及如何實現該含義。請參閱例如 http://code.activestate.com/recipes/498149/,它使用元類別主要在 Python 中重新建立 C 樣式結構。討論線程 [元類別的具體用例有哪些? ](https://codereview.stackexchange.com/questions/82786/what-are-some-concrete-use-cases-for-metaclasses) 也提供了一些範例,它們通常與聲明性程式設計有關,尤其是在ORM中使用。
在這種情況下,如果你使用你的方法2,並且一個子類別定義了一個__new__ 方法,它將在每次呼叫SubClassOfSingleton() 時執行,因為它負責呼叫傳回儲存的實例的方法。使用元類,它只會執行一次,即在創建唯一實例時。你需要自訂呼叫類別的定義,這是由其類型決定的。
一般來說,使用元類別來實現單例是有意義的。單例很特別,因為它的實例只創建一次,而元類別是自訂創建類別的實作方式,使其行為與普通類別不同。使用元類別可以讓你在其他方式需要自訂單例類別定義時有更多的控制權。
你的單例不需要多重繼承(因為元類不是基類),但對於繼承創建類的子類,你需要確保單例類是第一個/最左邊的元類,重新定義__call__。這不太可能有問題。實例字典並不在實例的名稱空間中,因此不會意外地覆寫它。
你還會聽到單例模式違反了"單一職責原則",即每個類別都應該只做一件事。這樣,你不必擔心在需要更改另一種程式碼時破壞程式碼所做的某一件事,因為它們是獨立且封裝的。元類實作通過了此測試。元類別負責強制模式,創建的類別和子類別不需要意識到它們是單例。 方法 1 沒有通過此測試,正如你用"MyClass 本身是一個函數,而不是類,因此無法調用類方法"指出的那樣。
在 Python2 和 3 中編寫程式碼需要使用稍微複雜一點的方案。由於元類通常是 type 類的子類,因此可以使用元類在運行時動態建立一個帶有它作為元類的中介基類,然後使用該基類作為公共單例基類的基類。這說起來比做起來難,如下:
def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass
這種方法的一個諷刺之處在於它使用子類化來實現元類。一個可能的優點是,與純元類別不同的是,isinstance(inst, Singleton) 將傳回 True。
關於另一個主題,你可能已經注意到了,但你原始貼文中的基類實作是錯誤的。需要在類別中引用 _instances,你需要使用 super(),或是類別方法的靜態方法,因為在呼叫時實際類別尚未建立。所有這些對於元類實作也是適用的。
class Singleton(object): _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton, BaseClass): pass
以上是在 Python 中實現單例的最佳方式是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!