Python:單例設計模式
1.單例模式的設計
- 設計模式
設計模式是前人工作的總結和提煉,通常被人們廣泛流傳的設計模式都是針對某一特定問題的成熟解決方法,相當於針對 某一問題已經有了解決套路,我們只需要知道套路即可
使用設計模式是為了可重用程式碼,讓程式碼容易被他人理解,保證程式碼可靠性
- 單例設計模式
目的 : 讓類建立的物件,在系統中只有唯一的一個例項
每一次執行 類名()返回的物件記憶體地址是相同的
- 單例模式的應用場景:
音樂播放器 物件:播放10收歌,只需要一個播放器即可
印表機 物件: 列印10份檔案,只需要一個印表機即可
顯然設計單例模式就是希望無論呼叫幾次類,只開闢一塊類物件的記憶體。那麼要開闢一塊記憶體,需要知道中那個方法在呼叫時會自動開闢記憶體,下面的__new__方法就是為物件分配空間,然後對記憶體new方法的改造即可達到單例模式的效果
2.__new__方法
- 使用 類名() 建立物件時,python的直譯器首先會呼叫 __new__方法為物件分配空間
- __new__是一個由object基類提供的內建的靜態方法,主要作用有兩個:
- 在記憶體中為物件分配空間
- 返回物件的引用
- Python的直譯器獲得物件的引用後,將引用作為第一個引數,傳遞給__init__方法
- 重寫__new__方法一定要return super().__new__(cls)
- 否則Python的直譯器得不到分配了空間物件引用,就不會呼叫物件的初始化方法
- 注意:__new__是一個靜態方法,在呼叫時需要主動傳遞cls引數
new方法的重寫程式碼:
class MusicPlayer: def __new__(cls,*args,**kwargs):#new方法的重寫 # 1.建立物件時,new 方法會被自動呼叫 print("建立方法,分配空間") # 2.為物件分配空間 instance=super().__new__(cls)#呼叫父類new方法,new方法是靜態方法,需要傳入引數cls # 3.返回物件的引用 return instance #必須要return,否則無法初始化 def __init__(self): print("播放器初始化") #建立播放器 player=MusicPlayer() print(player)#列印None 因為沒有return
3.python中單例模式
- 單例讓類建立的物件,在系統中只有一個唯一的例項
- 定義一個屬性類,初始值None,用於記錄單例模式的引用
- 重寫__new__方法
- 如果類屬性 is None ,呼叫父類方法分配空間,並在類屬性中記錄結果
- 返回類屬性中記錄的物件引用
如果不是單例模式則定義幾個物件,列印的地址都不相同如下圖:
在建立物件時,無論呼叫多少次建立物件的方法,永遠只是返回第一個建立物件的地址。
需要定義一個類屬性instance,通過如下流程圖即可完成單例模式的設計
程式碼:
class MusicPlayer:
# 記錄第一個被建立物件的引用
instance=None
def __new__(cls,*args,**kwargs):
# 1.判斷類屬性是否是空物件
if cls.instance is None:
# 2.呼叫父類方法,為第一個物件分配空間
cls.instance=super().__new__(cls)
# 3.返回類屬性儲存的物件引用
return cls.instance
player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)
#結果:
#<__main__.MusicPlayer object at 0x0000000009B83CC0>
#<__main__.MusicPlayer object at 0x0000000009B83CC0>
上面的程式碼雖然new了一塊記憶體,但是執行了多次初始化方法,那麼如何只呼叫一次初始化方法
只執行一次初始化方法:
在每次使用 類名()建立物件時,Python的直譯器會自動呼叫兩個方法:
__new__ 分配空間 __init__ 物件初始化
但在上面的程式碼修改之後,每次都會得到第一次被建立的引用,但是初始化方法還會被再次呼叫
如何讓初始化方法只執行一次:
1.定義一個類屬性init_flag 標識是否執行過初始化動作,初始值為false
2.在__init__方法中,判斷init_flag,如果為false就執行初始化動作
3.然後將init_flag 設定為true
4.這樣再次呼叫__init__方法時,初始化動作就不會被再次執行了
class MusicPlayer:
# 記錄第一個被建立物件的引用
instance=None
init_flag=False
def __new__(cls,*args,**kwargs):
# 1.判斷類屬性是否是空物件
if cls.instance is None:
# 2.呼叫父類方法,為第一個物件分配空間
cls.instance=super().__new__(cls)
# 3.返回類屬性儲存的物件引用
return cls.instance
def __init__(self):
# 1.判斷是否執行過初始化動作
if MusicPlayer.init_flag:
return
# 如果沒有執行過,在執行初始化動作
print("初始化播放器")
# 修改類屬性的標記
MusicPlayer.init_flag=True
player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)