有些時候你的專案中難免需要一些全域性唯一的物件,這些物件大多是一些工具性的東西,在Python中實現單例模式並不是什麼難事。以下總結幾種方法:
使用類裝飾器
使用裝飾器實現單例類的時候,類本身並不知道自己是單例的,所以寫程式碼的人可以不care這個,只要正常寫自己的類的實現就可以,類的單例有裝飾器保證。
1 2 3 4 5 6 7 8 |
def singleton(cls): instances = {} def _wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return _wrapper |
你會發現singleton
裝飾器內部使用了一個dict
。當然你也可以用其他的方式,不過以下的實現是錯誤的:
1 2 3 4 5 6 7 8 |
def singleton(cls): _instance = None #外部作用域的引用對於巢狀的內部作用域是隻讀的 def _wrapper(*args, **kwargs): if _instance is None: #直譯器會丟擲"UnboundLocalError: ...referenced before assignment" _instance = cls(*args, **kwargs) #賦值行為使直譯器將"_instance"看作區域性變數 return _instance return _wrapper |
使用元類(__metaclass__
)和可呼叫物件(__call__
)
Python的物件系統中一些皆物件,類也不例外,可以稱之為”型別物件”,比較繞,但仔細思考也不難:類本身也是一種物件,只不過這種物件很特殊,它表示某一種型別。是物件,那必然是例項化來的,那麼誰例項化後是這種型別物件呢?也就是元類。
Python中,class
關鍵字表示定義一個類物件,此時直譯器會按一定規則尋找__metaclass__
,如果找到了,就呼叫對應的元類實現來例項化該類物件;沒找到,就會呼叫type
元類來例項化該類物件。
__call__
是Python的魔術方法,Python的物件導向是”Duck type”的,意味著物件的行為可以通過實現協議來實現,可以看作是一種特殊的介面形式。某個類實現了__call__
方法意味著該類的物件是可呼叫的,可以想像函式呼叫的樣子。再考慮一下foo=Foo()
這種例項化的形式,是不是很像啊。結合元類的概念,可以看出,Foo
類是單例的,則在呼叫Foo()
的時候每次都返回了同樣的物件。而Foo
作為一個類物件是單例的,意味著它的類(即生成它的元類)是實現了__call__
方法的。所以可以如下實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Singleton(type): def __init__(cls, name, bases, attrs): super(Singleton, cls).__init__(name, bases, attrs) cls._instance = None def __call__(cls, *args, **kwargs): if cls._instance is None # 以下不要使用'cls._instance = cls(*args, **kwargs)', 防止死迴圈, # cls的呼叫行為已經被當前'__call__'協議攔截了 # 使用super(Singleton, cls).__call__來生成cls的例項 cls._instance = super(Singleton, cls).__call__(*args, **kwargs) return cls._instance class Foo(object): #單例類 __metaclass__ = Singleton >>>a = Foo() >>>b = Foo() >>>a is b >>>True >>>a.x = 1 >>>b.x >>>1 |
使用__new__
__init__
不是Python物件的構造方法,__init__
只負責初始化例項物件,在呼叫__init__
方法之前,會首先呼叫__new__
方法生成物件,可以認為__new__
方法充當了構造方法的角色。所以可以在__new__
中加以控制,使得某個類只生成唯一物件。具體實現時可以實現一個父類,過載__new__
方法,單例類只需要繼承這個父類就好。
1 2 3 4 5 6 7 8 9 |
class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance class Foo(Singleton): #單例類 a = 1 |