參考鏈接:
Python中的單例模式的幾種實現方式的及優化 - 聽風。 - 博客園
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
在 Python 中,我們可以用多種方法來實現單例模式
配置文件configuration.yml
DB:
host: 1
username: 12
password: 12
database: 14
databasetype: 15
port: 33
note: C
存儲配置文件的類MyDbinfo
import yaml
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, 'r', encoding='utf-8')
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x['DB']['host']
self.username=x['DB']['username']
self.password=x['DB']['password']
self.database=x['DB']['database']
self.port=x['DB']['port']
self.note = x['DB']['note']
現在創建2個MyDbinfo的實例
myDbinfo1 = MyDbinfo()
myDbinfo2 = MyDbinfo()
if __name__ == '__main__':
print(myDbinfo1)
print(myDbinfo2)
打印結果:
<MyDbinfo.MyDbinfo object at 0x7fcc580f4290>
<MyDbinfo.MyDbinfo object at 0x7fcc48021850>
打印結果,是2個不同的物理地址,則說明我們創建的實例,是2個不同的實例。會消耗內存資源。
其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc
文件,當第二次導入時,就會直接加載 .pyc
文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:
MyDbinfo.py
import yaml
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, 'r', encoding='utf-8')
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x['DB']['host']
self.username=x['DB']['username']
self.password=x['DB']['password']
self.database=x['DB']['database']
self.port=x['DB']['port']
self.note = x['DB']['note']
myDbinfo = MyDbinfo()
將上面的代碼保存在文件 MyDbinfo.py中,要使用時,直接在其他文件中導入此文件中的對象,這個對象即是單例模式的對象
創建2個實例
from MyDbinfo import myDbinfo
myDbinfo1 = myDbinfo
myDbinfo2 = myDbinfo
if __name__ == '__main__':
print(myDbinfo1)
print(myDbinfo2)
打印結果:
<MyDbinfo.MyDbinfo object at 0x7fc078111b90>
<MyDbinfo.MyDbinfo object at 0x7fc078111b90>
是單例的
singleton.py
def singleton(cls,*args,**kw):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args,**kw)
return instances[cls]
return _singleton
MyDbinfo類,加單例模式的注釋
MyDbinfo.py
import yaml
from singleton import singleton
@singleton
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, 'r', encoding='utf-8')
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x['DB']['host']
self.username=x['DB']['username']
self.password=x['DB']['password']
self.database=x['DB']['database']
self.port=x['DB']['port']
self.note = x['DB']['note']
開始測試
from MyDbinfo import MyDbinfo
myDbinfo1 = MyDbinfo()
myDbinfo2 = MyDbinfo()
if __name__ == '__main__':
print(myDbinfo1)
print(myDbinfo2)
打印結果:
<MyDbinfo.MyDbinfo object at 0x7fcd50154290>
<MyDbinfo.MyDbinfo object at 0x7fcd50154290>
為單例。
具體如何實現的:
1、需要先了解裝飾器:
python 閉包與裝飾器_傲嬌的喵醬的博客-CSDN博客
2、我們只需要關注這一塊邏輯
3、裡面的cls就是我們被裝飾的函數
4、_instance 是一個空字典
5、如果被裝飾的函數,不在字典裡,那我們就cls() 創建一個實例。放到字典裡,然後return出去。
如果被裝飾的函數,在字典裡,直接return出去這個實例。
6、閉包的作用就是讓一個變量能夠常駐內存。
則_instance = {} 是常駐內存的(這是重點)
②讓函數內部的局部變量始終保持在內存中:
怎麼來理解這句話呢?一般來說,函數內部的局部變量在這個函數運行完以後,就會被Python的垃圾回收機制從內存中清除掉。如果我們希望這個局部變量能夠長久的保存在內存中,那麼就可以用閉包來實現這個功能。
這裡借用
@千山飛雪
的例子(來自於:千山飛雪:深入淺出python閉包)。請看下面的代碼。
以一個類似棋盤游戲的例子來說明。假設棋盤大小為50*50,左上角為坐標系原點(0,0),我需要一個函數,接收2個參數,分別為方向(direction),步長(step),該函數控制棋子的運動。 這裡需要說明的是,每次運動的起點都是上次運動結束的終點。
def create(pos=[0,0]):
def go(direction, step):
new_x = pos[0]+direction[0]*step
new_y = pos[1]+direction[1]*step
pos[0] = new_x
pos[1] = new_y
return pos
return go
player = create()
print(player([1,0],10))
print(player([0,1],20))
print(player([-1,0],10))
在這段代碼中,player實際上就是閉包go函數的一個實例對象。
它一共運行了三次,第一次是沿X軸前進了10來到[10,0],第二次是沿Y軸前進了20來到 [10, 20],,第三次是反方向沿X軸退了10來到[0, 20]。
這證明了,函數create中的局部變量pos一直保存在內存中,並沒有在create調用後被自動清除。
為什麼會這樣呢?原因就在於create是go的父函數,而go被賦給了一個全局變量,這導致go始終在內存中,而go的存在依賴於create,因此create也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。
這個時候,閉包使得函數的實例對象的內部變量,變得很像一個類的實例對象的屬性,可以一直保存在內存中,並不斷的對其進行運算。
Project IntroductionThe system