Python中的单例模式的集中实现方式及优化
1067 字约 4 分钟
2024-12-01
单例模式
**单例模式(Singleton Pattern)**是一种常见的软件设计模式,主要目的是为了确保某一个类只有一个示例的存在。
例:某个服务器程序的配置信息存放在一个文件夹中,客户端通过一个AppConfig的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建AppConfig对象的实例,这样会导致系统中存在多个AppConfig的实例对象,严重浪费内存
实现单例模式的集中方法
1、使用模块
其实,Python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc
文件。 我们只需要在一个模块中定义函数和数据,就可以获得一个单例对象
class SingLeton:
def foo(self):
pass
singleton = SingLeton()
保存代码到文件mysingleton.py
中。
from mysingleton import singleton
2、使用装饰器
def sing_leton(cls):
_instance = {}
def singleton(*args,**kwargs):
if cls not in _instance:
_instance[cls] = cls(*args,**kwargs)
return singleton
@sing_leton
class A:
a = 1
def __init__(self,x=0):
self.x = x
a1 = A(1)
a2 = A(2)
3、使用类
class sing_leton:
def __init__(self):
pass
@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance
当然一般情况下,大家以为这样就可以完成单例模式了,但是这样在使用多线程的时候会产生线程不安全
import threading
def task(arg):
obj = sing_leton.get_instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
程序执行↓
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
看起来好像也没有什么毛病,但是真的没有毛病吗?,我们来修改一下相关的代码
class sing_leton:
def __init__(self):
import time
time.sleep(1)
@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance
执行得到了这样的结果。
<__main__.sing_leton object at 0x000001ED128C4438><__main__.sing_leton object at 0x000001ED128C4F60>
<__main__.sing_leton object at 0x000001ED128C4898><__main__.sing_leton object at 0x000001ED128C45C0>
<__main__.sing_leton object at 0x000001ED128C4F28><__main__.sing_leton object at 0x000001ED128C46D8>
<__main__.sing_leton object at 0x000001ED128C4588><__main__.sing_leton object at 0x000001ED1290EC50>
<__main__.sing_leton object at 0x000001ED128C4550><__main__.sing_leton object at 0x000001ED1290EA58>
那么问题出现了,创建的单例模式,却无法支持多线程的访问。那有什么解决方案呢。
** 解决方法:** 加锁啊!加锁的部分串行,不加锁部分并行。保证了安全。
class sing_leton:
_instance_lock = threading.Lock()
def __init__(self):
import time
time.sleep(2)
@classmethod
def get_instance(cls,*args,**kwargs):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance
执行如下:
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
但是这里还有一个小的问题,就是当我们下一次进行性实例化对象的时候,已经史丹利模式了,但是我们还是加了锁,这样不太好,在进行一些优化。
class sing_leton:
_instance_lock = threading.Lock()
def __init__(self):
import time
time.sleep(2)
@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance
这种单例模式会有限制,以后实例化必须通过 instance = sing_leton.get_instance()
4、基于__new__方法实现(推荐)
当我们实例化一个对象的时,先执行类的__new__
方法(没有定义__new__
的时候,默认object.__new__
),实例化对象;然后执行类的__init__方法,对这个对象进行初始化。
class sing_leton:
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance
如果是采用这种方法的话,实例化对象的时候和平时就一样了。instance = sing_leton()
5、基于metaclass方式实现
1、类由type创建,创建类时,type的__init__方法自动执行,类()执行type的__call__方法(类的__new__方法,类的__init__方法)
2、对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法
class sing_leton_Type(type):
_instance_lock = threading.Lock()
def __call__(cls,*args,**kwargs):
if not hasattr(cls,"_instance"):
with sing_leton_Type._instance_lock:
if not hasattr(cls,"_instance"):
cls._instance = super(sing_leton_Type,cls).__call__(*args,**kwargs)
return cls._instance
class Foo(metaclass=sing_leton_Type):
def __init__(self,name):
self.naem = name