課程簡介
“讀萬卷書不如走萬里路”,設計模式學不好的根本,還是在于缺少實戰,
新課《Python 設計模式基礎實戰》向大家介紹了編程語言的設計模式,著重強調大家動手實際操作,
總苦于無法將設計模式活學活用的你,不妨來學一下,
設計模式簡介
所謂“設計模式”就是一套由前人總結的代碼的設計思路,以其中最常用的“單例模式”為例,在程式中我們有一個需求,這個需求的實作有多種思路,其中一種是創建一個類并且使得該類在多次實體化時生成唯一的一個實體,這就需要設計代碼實作這個結果,大家發現這種場景下這樣設計是最合理的,我們就管這種設計思路叫做“單例模式”,
設計模式不分語言,大多數編程語言都可以實作,
一個設計模式并不像一個類或一個庫那樣能夠直接作用于我們的代碼,拿建造橋梁粗略地類比一下,一座橋是一個小功能,代碼就是磚石瓦塊鋼筋水泥,設計模式就是我們要怎么建造,圓拱橋、獨木橋、管道橋還是拉索橋,
設計模式很有用,但它要用到合適的場景中才能發揮應有的效果,否則可能出現弊大于利的情況,
通常來講設計模式分為三類:
- 創建模式,提供實體化的方法,為適合的狀況提供相應的物件創建方法,
- 結構模式,通常用來處理物體之間的關系,使得這些物體能夠更好地協同作業,
- 行為模式,用于在不同的物體建進行通信,為物體之間的通信提供更容易、更靈活的通信方法,
單例模式
所謂單例模式,也就是說任何時候我們都要確保只有一個物件實體存在,很多情況下,整個系統中只需要存在一個物件,所有的資訊都從這個物件獲取,比如系統的配置物件,或者是執行緒池,這些場景下,就非常適合使用單例模式,
總結起來,就是說不管我們實體化一個類多少次,真正干活的物件只會生成一次并且在首次實體化時生成,
在 Python 中實作單例模式的方式有很多,下面分別舉例說明,
1)使用嵌套類實作
在定義類 A 時,在類中再定義一個嵌套類 _A,首次對類 A 進行實體化時,將類 _A 的實體賦值給類 A 的屬性 _instance ,然后給類 A 的實體定義一個 __getattr__ 方法,使得類 A 的實體呼叫自身屬性或方法時,都去呼叫類的 _instance 屬性,也就是類 _A 的實體的屬性和方法,類屬性是固定不變的,所以類 A 的實體雖然是不同的,但它們的屬性和方法都是完全一樣的,
將如下代碼寫入 singleton_1.py 檔案中:
class Singleton:
'''
單例模式
'''
# 創建一個嵌套類
class _A:
def display(self): # 1
return id(self)
_instance = None
def __init__(self): # 2
__class__._instance = __class__._instance or __class__._A()
def __getattr__(self, attr): # 3
return getattr(__class__._instance, attr)
def __setattr__(self, attr, value): # 4
object.__setattr__(__class__._instance, attr, value)
if __name__ == '__main__':
s1 = Singleton(); s2 = Singleton() # 5
print('id(s1):', id(s1)) # 6
print('id(s2):', id(s2))
print('s1.display():', s1.display()) # 7
print('s2.display():', s2.display())
s1.name = 'James' # 8
print('s1.name:', s1.name)
print('s2.name:', </pre>
如上所示,對代碼進行簡略說明:
# 1創建一個嵌套類 _A,在類內部定義一個 display 方法,該方法回傳 _A 類的實體的記憶體地址,# 2撰寫 Singleton 類的實體的初始化方法,創建 Singleton 類的實體后,執行此方法,方法內部是對類的操作,為類的 _instance 屬性賦值一個 _A 的實體,第一次對 Singleton 進行實體化時會創建一個 _A 類的實體并賦值,以后不再變化,# 3撰寫 Singleton 類的實體獲取屬性的方法,Singleton 類內部故意不為自身的實體設定任何屬性,結果就是呼叫實體的屬性時最后落到此方法的頭上,方法內部獲取類屬性 _instance 的同名屬性,也就是 _A 類的實體的屬性,# 4撰寫 Singleton 類的實體定義屬性的方法,同樣,此方法內部呼叫object.__setattr__方法為 Singleton._instance 也就是 _A 的實體定義屬性,# 5為 Singleton 類創建兩個實體以備測驗,# 6列印兩個實體的記憶體地址,它們的結果應該是不同的,# 7列印兩個實體呼叫 display 方法的結果,實際上呼叫的都是Singleton._instance的同名方法,結果應該是一樣的,# 8其中一個實體定義 name 屬性,然后兩個實體獲取該屬性并列印,結果應該都是一樣的,
終端執行腳本,操作結果如下所示:
$ python3 singleton_1.py
id(s1): 4330824208
id(s2): 4330824336
s1.display(): 4330824272
s2.display(): 4330824272
s1.name: James
s2.name: James
如上所示,Singleton 的實體各不相同,它們在賦值屬性和呼叫屬性時,結果卻是相同的,因為這些實體操作屬性時都轉移到了嵌套類 _A 的實體上,
2)使用裝飾器實作
以上代碼雖然很好地實作了單例模式,但是在真正的專案開發中這種方式卻不夠靈活,因為我們要將真正干活的類內置在單例類中,這會有些麻煩,例如洗掉實體的屬性這一點就不太好實作,
下面我們使用 Python 裝飾器來實作單例模式:
首先創建一個「類裝飾器」,也就是撰寫一個類,這個類作為裝飾器,這個「類裝飾器」在實體化的時候,將另一個類作為引數,類裝飾器的名字是 SingletonDeco ,其 __call__ 方法就是實體呼叫自身所執行的方法,我們可以把此方法的回傳值定義為唯一的物件,
將如下代碼寫入 singleton_2.py 檔案中:
class SingletonDeco:
"""
單例類裝飾器
"""
def __init__(self, cls): # 1
print('裝飾器初始化')
self._cls = cls
def instance(self): # 2
try:
return self._instance
except AttributeError:
self._instance = self._cls()
return self._instance
def __call__(self): # 3
return self.instance()
@SingletonDeco # 4
class Singleton:
def display(self):
return id(self)
if __name__ == '__main__':
s1 = Singleton() # 5
s2 = Singleton()
print('id(s1):', s1.display())
print('id(s2):', s2.display())
print('s1 is s2:', s1 is s2)
對代碼進行簡單描述:
# 1類裝飾器的初始化方法,將被裝飾的類賦值給實體的 _cls 屬性,# 2此方法用于給實體的 _instance 屬性賦值,此方法的呼叫權交個了__call__方法,也就是說呼叫 SingletonDeco 類的實體時會執行 instance 方法并回傳被裝飾器裝飾的類 Singleton 的實體,并且不論呼叫多少次,結果都是一樣的,# 3類裝飾器 SingletonDeco 的實體的呼叫介面,# 4使用類裝飾器創建 Singleton 類,創建該類時,會執行SingletonDeco.__init__方法,并且將該類賦值給實體的 _cls 屬性,此時 Singleton 這個變數就指向了 SingletonDeco 這個類的實體,如果要獲取原 Singleton 類,就需要呼叫 Singleton 的 _cls 屬性,此外原 Singleton 類為實體提供了 display 方法回傳實體的記憶體地址,# 5呼叫 Singleton ,表面上看是對 Singleton 類進行實體化,實際上是呼叫 SingletonDeco 類的實體的__call__方法,因為變數 Singleton 指向的就是 SingletonDeco 的實體,呼叫__call__的結果就是呼叫instance方法,下一步就是對呼叫實體的_cls屬性,而這個屬性的值就是原 Singleton 類,綜上所述,這個最終的呼叫結果還是原 Singleton 的實體,繞這么大一圈,一切都是為了在 instance 方法中實作唯一實體,
終端執行結果如下:
$ python3 singleton_2.py
裝飾器初始化
id(s1): 4350000976
id(s2): 4350000976
s1 is s2: True
以上代碼中,我們用裝飾器實作了單例模式,任何想使用單例模式的類,只需要使用 Singleton 裝飾器裝飾一下就可以使用了,
可以看到其核心作業原理其實和第一種實作方式是一致的,也是使用內置的屬性 Singleton._instance 來存盤實體的,通過使用裝飾器的模式我們將代碼解耦了,使用更加靈活,
其實這里我們也用到裝飾者模式啦,后面的章節會介紹,
3)重寫 new 方法
在對類進行實體化時,需要先呼叫類的 __new__ 方法創建實體,再呼叫實體的 __init__ 方法初始化,所以要實作單例模式,可以在類的 __new__ 方法中做文章,
首先判斷類屬性 __instance 是否存在,注意,使用 hasattr 方法時,相當于在類的外部呼叫類屬性,私有屬性的命令是一個下劃線加類名加屬性名,如果該屬性不存在,呼叫根父類 object 的 __new__ 生成一個 Singleton 類的實體并賦值給類屬性 __instance ,接下來列印類屬性 __instance 的記憶體地址,實際上就是類的實體的記憶體地址,最后回傳該實體,
在對類進行實體化時,只有首次會創建類的實體,之后都是回傳類的 __instance 屬性值,這樣設計就可以實作單例模式了,
將如下代碼寫入 singleton_3.py 檔案:
class Singleton:
def __new__(cls, *args, **kw):
if not hasattr(cls, '_Singleton__instance'):
cls.__instance = super().__new__(cls, *args, **kw)
print('實體化時列印實體 ID:', id(cls.__instance))
return cls.__instance
s1 = Singleton()
s2 = Singleton()
print('s1 is s2:', s1 is s2)
其中 super().__new__ 等同于 object.__new__ ,
如果不重寫類的 __new__ 方法,則默認呼叫 object 的同名方法并回傳,也就是每次都會創建一個新的實體,重寫的目的就是將一個實體固定到類屬性中,然后每次創建實體時都回傳這個屬性值,這個方式思路簡單,代碼也很清晰,
終端執行程式結果如下:
$ python3 singleton_3.py
實體化時列印實體 ID: 4480060240
實體化時列印實體 ID: 4480060240
s1 is s2: True
總結
本節實驗內容來自于《Python 設計模式基礎實戰》,主要介紹了設計模式的基本概念,并對創建型模式之一單例模式的實作進行了講解,其中涉及到三個方法,它們可以根據實際場景進行恰當地選用,所有的設計都是為了實作對某個類的實體進行唯一的限制,
后續實驗我們將講解其他模式,以及一些額外的關于“元類”的知識等,
你將學到:
![]()
點擊《Python 設計模式基礎實戰》,學習完整課程,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/125469.html
標籤:Python
上一篇:陣列的復制
下一篇:figure影像
