python實現單例模式的幾種方式_基于Python中單例模式的幾種實現方式及優化詳解...

單例模式

單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。

比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。

在 Python 中,我們可以用多種方法來實現單例模式

實現單例模式的幾種方式

1.使用模塊

其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc 文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:

mysingleton.py

class Singleton(object):

def foo(self):

pass

singleton = Singleton()

將上面的代碼保存在文件 mysingleton.py 中,要使用時,直接在其他文件中導入此文件中的對象,這個對象即是單例模式的對象

from a import singleton

2.使用類

class Singleton(object):

def __init__(self):

pass

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

一般情況,大家以為這樣就完成了單例模式,但是這樣當使用多線程時會存在問題

class Singleton(object):

def __init__(self):

pass

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

import threading

def task(arg):

obj = Singleton.instance()

print(obj)

for i in range(10):

t = threading.Thread(target=task,args=[i,])

t.start()

程序執行后,打印結果如下:

看起來也沒有問題,那是因為執行速度過快,如果在init方法中有一些IO操作,就會發現問題了,下面我們通過time.sleep模擬

我們在上面__init__方法中加入以下代碼:

def __init__(self): import time time.sleep(1)

重新執行程序后,結果如下

問題出現了!按照以上方式創建的單例,無法支持多線程

解決辦法:加鎖!未加鎖部分并發執行,加鎖部分串行執行,速度降低,但是保證了數據安全

import time

import threading

class Singleton(object):

_instance_lock = threading.Lock()

def __init__(self):

time.sleep(1)

@classmethod

def instance(cls, *args, **kwargs):

with Singleton._instance_lock:

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

def task(arg):

obj = Singleton.instance()

print(obj)

for i in range(10):

t = threading.Thread(target=task,args=[i,])

t.start()

time.sleep(20)

obj = Singleton.instance()

print(obj)

打印結果如下:

這樣就差不多了,但是還是有一點小問題,就是當程序執行時,執行了time.sleep(20)后,下面實例化對象時,此時已經是單例模式了,但我們還是加了鎖,這樣不太好,再進行一些優化,把intance方法,改成下面的這樣就行:

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

with Singleton._instance_lock:

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

這樣,一個可以支持多線程的單例模式就完成了

import time

import threading

class Singleton(object):

_instance_lock = threading.Lock()

def __init__(self):

time.sleep(1)

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

with Singleton._instance_lock:

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

def task(arg):

obj = Singleton.instance()

print(obj)

for i in range(10):

t = threading.Thread(target=task,args=[i,])

t.start()

time.sleep(20)

obj = Singleton.instance()

print(obj)

這種方式實現的單例模式,使用時會有限制,以后實例化必須通過 obj = Singleton.instance()

如果用 obj=Singleton() ,這種方式得到的不是單例

3.基于__new__方法實現(推薦使用,方便)

通過上面例子,我們可以知道,當我們實現單例時,為了保證線程安全需要在內部加入鎖

我們知道,當我們實例化一個對象時,是先執行了類的__new__方法(我們沒寫時,默認調用object.__new__),實例化對象;然后再執行類的__init__方法,對這個對象進行初始化,所有我們可以基于這個,實現單例模式

import threading

class Singleton(object):

_instance_lock = threading.Lock()

def __init__(self):

pass

def __new__(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

with Singleton._instance_lock:

if not hasattr(Singleton, "_instance"):

Singleton._instance = object.__new__(cls, *args, **kwargs)

return Singleton._instance

obj1 = Singleton()

obj2 = Singleton()

print(obj1,obj2)

def task(arg):

obj = Singleton()

print(obj)

for i in range(10):

t = threading.Thread(target=task,args=[i,])

t.start()

打印結果如下:

采用這種方式的單例模式,以后實例化對象時,和平時實例化對象的方法一樣 obj = Singleton()

4.基于metaclass方式實現

相關知識

"""

1.類由type創建,創建類時候type的__init__方法自動執行,類() 執行type的 __call__方法(類的__new__方法,類的__init__方法)

2.對象由類創建,創建對象時候類的__init__方法自動執行,對象()執行類的 __call__ 方法

"""

例子:

class Foo:

def __init__(self):

pass

def __call__(self, *args, **kwargs):

pass

obj = Foo()

# 執行type的 __call__ 方法,調用 Foo類(是type的對象)的 __new__方法,用于創建對象,然后調用 Foo類(是type的對象)的 __init__方法,用于對對象初始化。

obj() # 執行Foo的 __call__ 方法

元類的使用

class SingletonType(type):

def __init__(self,*args,**kwargs):

super(SingletonType,self).__init__(*args,**kwargs)

def __call__(cls, *args, **kwargs): # 這里的cls,即Foo類

print('cls',cls)

obj = cls.__new__(cls,*args, **kwargs)

cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)

return obj

class Foo(metaclass=SingletonType): # 指定創建Foo的type為SingletonType

def __init__(self):

pass

def __new__(cls, *args, **kwargs):

return object.__new__(cls, *args, **kwargs)

obj = Foo()

實現單例模式

import threading

class SingletonType(type):

_instance_lock = threading.Lock()

def __call__(cls, *args, **kwargs):

if not hasattr(cls, "_instance"):

with SingletonType._instance_lock:

if not hasattr(cls, "_instance"):

cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)

return cls._instance

class Foo(metaclass=SingletonType):

def __init__(self,name):

self.name = name

obj1 = Foo('name')

obj2 = Foo('name')

print(obj1,obj2)

以上這篇基于Python中單例模式的幾種實現方式及優化詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持我們。

本文標題: 基于Python中單例模式的幾種實現方式及優化詳解

本文地址: http://www.cppcns.com/jiaoben/python/217200.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372402.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372402.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372402.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

android-鈴聲的設置與播放

在android系統中,不同鈴聲存放的鈴聲路徑:/system/media/audio/ringtones 來電鈴聲/system/media/audio/notifications 短信通知鈴聲/system/media/audio/alarms 鬧鐘鈴聲鈴聲的設置:import java.io.File; import andr…

Apache Commons SCXML:有限狀態機實現

本文提到有限狀態機(FSM),SCXML(狀態圖可擴展標記語言)和Apache Common的SCXML庫。 本文還提供了基本的ATM有限狀態機示例代碼。 有限狀態機: 您可能還記得計算機科學課程中的有限狀態機。 FSM用于設計計算…

第二十章、分離應用程序邏輯并處理事件

理解委托 委托是對方法的引用。(之所以稱為委托,是因為一旦被調用,就將具體的處理“委托”給引用的方法) 委托對象引用了方法,和將int賦值給int變量一樣,是將方法引用賦給委托對象。 Processor p new Proc…

pymol怎么做底物口袋表面_怎么從文獻中發掘一篇新文章?

本文來自微信公眾號:X-MOLNews可能你的導師也曾說過這樣的話——盯著Nature、Science級別的文章做,可能最終會中十分的文章;如果盯著十分的文章做,可能最終發出來也就五六分;但如果就為了發個文章混畢業,很…

如何分析線程轉儲– IBM VM

本文是我們的線程轉儲分析系列的第4部分,它將為您概述什么是IBM VM的JVM線程轉儲以及您將找到的不同線程和數據點。 您將看到和學習??到,IBM VM Thread Dump格式是不同的,但是提供了更多現成的故障排除數據。 在這一點上,您應該…

VMware vSphere克隆虛擬機

參考資料:http://blog.csdn.net/shen_jz2012/article/details/484167711. 首先將你所要克隆的虛擬機關掉2. 選擇你的ESXI服務器選中"配置",然后選中存儲器右鍵你的存儲介質,比如我的是datastore1,選擇“瀏覽數據存儲”。…

將本地jar包倒入maven項目類庫中

有兩種方法&#xff1a;1.本地下載maven并配置環境變量&#xff0c;然后運行cmd控制臺輸入 mvn install:install-file -Dfile本地jar路徑 -DgroupId -DartifactId -Dpackagingjar -Dversion -DgeneratePomtrue. 2.直接在pom.xml中對應的依賴下面添加<scope>system&l…

Spring和JSF集成:分頁

處理大型數據集時&#xff0c;通常需要以分頁格式顯示數據。 分頁是一個有趣的問題&#xff0c;因為它傾向于跨越應用程序的所有層&#xff0c;從視圖層通過應用程序服務一直到對數據庫的原始調用。 在獲取分頁數據時&#xff0c;有一些非常好的解決方案。 如果您使用的是JPA&a…

三重積分平均值_直角坐標系下的三重積分的幾何可視化解釋圖解高等數學

12.4 直角坐標系下的三重積分三重積分假設 F(x,y,z) 為一個空間有界閉區域 D 上的函數. D 為下面立體橢球所占區域. 將空間區域分割成小長方塊. 體積記為 ΔVk, 其長寬高分別為Δxk, Δyk, Δzk , 并有下列的求和式:觀察下面動畫, 當空間不斷分割, 每個小方塊的體積 ΔVk 不斷變…

最短網絡Agri-Net

【例4-11】、最短網絡Agri-Net【問題描述】農民約翰被選為他們鎮的鎮長&#xff01;他其中一個競選承諾就是在鎮上建立起互聯網&#xff0c;并連接到所有的農場。當然&#xff0c;他需要你的幫助。約翰已經給他的農場安排了一條高速的網絡線路&#xff0c;他想把這條線路共享給…

cors-synchronous-requests-not-working-in-firefox

http://stackoverflow.com/questions/16668386/cors-synchronous-requests-not-working-in-firefox轉載于:https://www.cnblogs.com/diyunpeng/p/5829594.html

硬盤接口協議

硬盤是電腦主要的存儲媒介之一&#xff0c;由一個或者多個鋁制或者玻璃制的碟片組成。碟片外覆蓋有鐵磁性材料。硬盤有固態硬盤&#xff08;SSD 盤&#xff0c;新式硬盤&#xff09;、機械硬盤&#xff08;HDD 傳統硬盤&#xff09;、混合硬盤&#xff08;HHD 一塊基于傳統機械…

圖的表示

Python 數據結構與算法——圖&#xff08;Graph&#xff09; 1. 鄰接矩陣 vs 鄰接表&#xff08;壓縮的鄰接矩陣&#xff09; 鄰接矩陣的缺點是&#xff1a; 空間占用與結點數的平方成正比&#xff0c;可能帶來很大的浪費&#xff1b;鄰接矩陣不容易增加新的結點&#xff0c;不…

在Java Web應用程序中阻止CSRF

跨站點請求偽造攻擊&#xff08;CSRF&#xff09;在Web應用程序中非常常見&#xff0c;如果允許&#xff0c;可能會造成重大危害。 如果您從未聽說過CSRF&#xff0c;建議您查看有關它的OWASP頁面 。 幸運的是&#xff0c;阻止CSRF攻擊非常簡單&#xff0c;我將向您展示它們的工…

windows命令行無法啟動redis_windows系統安裝redis

1、下載最新redis https://github.com/MicrosoftArchive/redis/releases我選擇下載msi版本的2.雙擊下載包安裝3.設置redis環境變量&#xff0c;把redis路徑配置到系統變量path值中4啟動redis&#xff0c;cmd進入安裝好redis文件夾 輸入&#xff1a;如果redis啟動出錯Creating S…

SQL Server 篩選時間區間

一、SQL直接判斷 select * from login where pass>2013/03/25 and pass < 2017/04/24 二、DATEDIFF() 函數返回兩個日期之間的時間 --語法 DATEDIFF(datepart,startdate,enddate) --開始時間 startdate --結束時間 enddate --datepart datepart縮寫年yy, yyyy季度qq, …

OpenShift Express Web管理控制臺:入門

本周&#xff0c; 最新版本的OpenShift為已經很棒的PaaS Cloud提供商帶來了兩個非常好的功能。 首先&#xff0c;JBoss AS已從7.0升級到7.1&#xff0c;并且所有新的Express Web Management Console已作為預覽發布。 在本文中&#xff0c;我們將研究如何使用此新控制臺&#xf…

Linux-IP地址后邊加個/8(16,24,32)是什么意思?

是掩碼的位數 A類IP地址的默認子網掩碼為255.0.0.0&#xff08;由于255相當于二進制的8位1&#xff0c;所以也縮寫成“/8”&#xff0c;表示網絡號占了8位&#xff09;; B類的為255.255.0.0&#xff08;/16&#xff09;; C類的為255.255.255.0(/24) /30就是255…

女士細線毛衣起多少針_從起針到縫合,教你織毛衣的各種要點(詳細教程)

新手學織毛衣看過來&#xff0c;7大編織要點幫你解決織好一件毛衣的基礎問題&#xff0c;滿滿的干貨&#xff0c;每點都值得學習!一、起針二、棒針符號三、如何織小樣四、依據小樣推算針數收掛肩的推算五、斜肩針數的推算開前、后領的位置與針數六、袖山的推算七、如何上袖子一…

關于OPENSSL的使用

#import <Foundation/Foundation.h> interface RSAEncryptor : NSObject /** * 加密方法 * * param str 需要加密的字符串 * param path .der格式的公鑰文件路徑 */ (NSString *)encryptString:(NSString *)str publicKeyWithContentsOfFile:(NSString *)path; /*…