程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python 單例模式

編輯:Python

 參考鏈接:

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個不同的實例。會消耗內存資源。

二、實現單例模式的幾種方式

1.使用模塊

其實,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>

是單例的

2.使用裝飾器

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)回收。

這個時候,閉包使得函數的實例對象的內部變量,變得很像一個類的實例對象的屬性,可以一直保存在內存中,並不斷的對其進行運算。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved