在Python中實現單例模式

發表於2017-03-23

有些時候你的專案中難免需要一些全域性唯一的物件,這些物件大多是一些工具性的東西,在Python中實現單例模式並不是什麼難事。以下總結幾種方法:

使用類裝飾器

使用裝飾器實現單例類的時候,類本身並不知道自己是單例的,所以寫程式碼的人可以不care這個,只要正常寫自己的類的實現就可以,類的單例有裝飾器保證。

你會發現singleton裝飾器內部使用了一個dict。當然你也可以用其他的方式,不過以下的實現是錯誤的:

使用元類(__metaclass__)和可呼叫物件(__call__)

Python的物件系統中一些皆物件,類也不例外,可以稱之為”型別物件”,比較繞,但仔細思考也不難:類本身也是一種物件,只不過這種物件很特殊,它表示某一種型別。是物件,那必然是例項化來的,那麼誰例項化後是這種型別物件呢?也就是元類。

Python中,class關鍵字表示定義一個類物件,此時直譯器會按一定規則尋找__metaclass__,如果找到了,就呼叫對應的元類實現來例項化該類物件;沒找到,就會呼叫type元類來例項化該類物件。

__call__是Python的魔術方法,Python的物件導向是”Duck type”的,意味著物件的行為可以通過實現協議來實現,可以看作是一種特殊的介面形式。某個類實現了__call__方法意味著該類的物件是可呼叫的,可以想像函式呼叫的樣子。再考慮一下foo=Foo()這種例項化的形式,是不是很像啊。結合元類的概念,可以看出,Foo類是單例的,則在呼叫Foo()的時候每次都返回了同樣的物件。而Foo作為一個類物件是單例的,意味著它的類(即生成它的元類)是實現了__call__方法的。所以可以如下實現:

使用__new__

__init__不是Python物件的構造方法,__init__只負責初始化例項物件,在呼叫__init__方法之前,會首先呼叫__new__方法生成物件,可以認為__new__方法充當了構造方法的角色。所以可以在__new__中加以控制,使得某個類只生成唯一物件。具體實現時可以實現一個父類,過載__new__方法,單例類只需要繼承這個父類就好。

相關文章