Python學習筆記:單例模式

單例模式:一個類不管實例化多少次,返回的都是同一個實例,例如:a1=A(), a2=A(), a3=A(),a一、a2和a3其實都是同一個對象,即print(a1 is a2)和print(a2 is a3)都會打印True。html

實現方式:有兩種方式,一種是使用元類metaclass控制類實例化時的對象,另外一種是使用類的__new__方法控制類返回的對象,推薦使用元類的方式,由於__new__一般是用來改變類結構的。eclipse

注:關於元類和單例模式,本文只是貼了兩個簡單的示例代碼和本身的一些心得,想要更加深刻的學習,這裏有一篇博客講得很詳細https://www.cnblogs.com/tkqasn/p/6524879.html學習

 

元類實現單例模式(Python3.6):spa

 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 ----------輸出結果----------
24 hello
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):code

 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 hello
21 {'val': 'hello'}
22 hi
23 {'val': 'hi'}
24 True
  • 原理:經過給類定義一個類變量,指向本類的一個實例,每次實例化調用__new__的時候都返回這個類變量,能夠看到數據結果打印的是True,因此天然就是單例了。
  • 缺點:每次實例化雖然都是同一個實例,可是每次實例化都會調用一次__init__方法,致使這個實例會隨着每次初始化而改變,因此不推薦這種方式來實現單例,由於__new__方法通常是用來改變類結構的。
  • hasattr:類中的私有變量,即加了雙下劃線的變量,在__dict__中會加上一個「_classname」前綴,因此若是這裏使用__instance的話,hasattr(cls, '__instance')會一直返回False,由於這裏已經不是__instance了,而是_MySingleton__instance。
相關文章
相關標籤/搜索