Python學習筆記:單例模式
阿新 • • 發佈:2018-10-21
turn 實例 提示 type類 metaclass let 所有 python3.6 ips
單例模式:一個類無論實例化多少次,返回的都是同一個實例,例如:a1=A(), a2=A(), a3=A(),a1、a2和a3其實都是同一個對象,即print(a1 is a2)和print(a2 is a3)都會打印True。
實現方式:有兩種方式,一種是使用元類metaclass控制類實例化時的對象,另一種是使用類的__new__方法控制類返回的對象,推薦使用元類的方式,因為__new__通常是用來改變類結構的。
註:關於元類和單例模式,本文只是貼了兩個簡單的示例代碼和自己的一些心得,想要更加深入的學習,這裏有一篇博客講得很詳細https://www.cnblogs.com/tkqasn/p/6524879.html
元類實現單例模式(Python3.6):
1 class Singleton(type): 2 def __init__(cls, *args, **kwargs): 3 cls.__instance = None 4 super().__init__(*args, **kwargs) 5 6 def __call__(cls, *args, **kwargs): 7 if cls.__instance is None: 8 cls.__instance = super().__call__(*args, **kwargs) 9 10 return cls.__instance 11 12 13 class MySingleton(metaclass=Singleton): 14 def __init__(self, val): 15 self.val = val 16 print(self.val) 17 18 19 hello = MySingleton(‘hello‘) 20 hi = MySingleton(‘hi‘) 21 print(hello is hi) 22 23 ----------輸出結果---------- 24hello 25 True
- metaclass的使用:Python3中metaclass是通過指定metaclass實現的,Python2中是通過指定類變量__metaclass__來實現的,但原理都是一樣的。
- 知識點type:Python中所有的類都是type類的實例,即一個類(還未實例化)的定義,其實就是type類(Python內建元類)的實例。如下的打印可以更加直觀的理解這一點:
>>> int.__class__ <class ‘type‘> >>> num = 3 >>> num.__class__ <class ‘int‘> >>> num.__class__.__class__ <class ‘type‘> >>> >>> >>> class A: pass >>> A.__class__ <class ‘type‘> >>> a = A() >>> a.__class__ <class ‘__main__.A‘> >>> a.__class__.__class__ <class ‘type‘> >>>
- 知識點__call__:當調用一個實例時,即執行實例加括號的形式,就會調用該實例的__call__方法,如果沒有定義(需要自己定義),則會報錯。例如a=A(),a()就會調用a的__call__方法。
- 代碼執行流程:第一步執行MySingleton時(即沒加括號的部分),進行元類的實例化,即MySingleton=Singleton(),Singleton的實例化和普通類一樣會先執行__new__返回該類的實例,然後自動執行該實例的__init__方法進行初始化,此示例中的初始化方法給該實例賦予了一個值為None的__instance變量;第二步執行MySingleton(‘hello‘)時,進行類的實例化,即MySingleton(‘hello‘)=Singleton()(‘hello‘),這裏就會調用到Singleton的__call__方法了,而super().__call__即調用type的__call__方法,這時候就和普通類實例化一樣會調用MySingleton的__new__和__init__方法了。
- 原理:由於每次實例化MySingleton時都會先調用metaclass中的__call__方法,所以只有第一次實例化時才會執行MySingleton的__new__和__init__,後面的實例化都只會返回第一次實例化好的實例,所以導致的結果就是無論進行多少次實例化,都給你返回同一個實例,當然就只有單例了(所以“輸出結果”中就沒有打印“hi”了)
- cls和self:Singleton的編寫,在eclipse中提示需要寫成self,在PyCharm中提示需要寫成cls,因為參數self是約定代表實例本身,但在這裏type的實例就是類,所以推薦寫成cls。
__new__實現單例模式(Python3.6):
1 class MySingleton: 2 def __init__(self, val): 3 self.val = val 4 print(self.val) 5 print(self.__dict__) 6 7 def __new__(cls, *args, **kwargs): 8 if not hasattr(cls, ‘_instance‘): 9 cls._instance = super().__new__(cls) 10 11 return cls._instance 12 13 14 hello = MySingleton(‘hello‘) 15 hi = MySingleton(‘hi‘) 16 print(hello is hi) 17 18 19 -----------輸出結果-------------- 20 hello21 {‘val‘: ‘hello‘}22 hi23 {‘val‘: ‘hi‘}24 True
- 原理:通過給類定義一個類變量,指向本類的一個實例,每次實例化調用__new__的時候都返回這個類變量,可以看到數據結果打印的是True,所以自然就是單例了。
- 缺點:每次實例化雖然都是同一個實例,但是每次實例化都會調用一次__init__方法,導致這個實例會隨著每次初始化而改變,所以不推薦這種方式來實現單例,因為__new__方法一般是用來改變類結構的。
- hasattr:類中的私有變量,即加了雙下劃線的變量,在__dict__中會加上一個“_classname”前綴,所以如果這裏使用__instance的話,hasattr(cls, ‘__instance‘)會一直返回False,因為這裏已經不是__instance了,而是_MySingleton__instance。
Python學習筆記:單例模式