当前位置: 首页 > 后端技术 > Python

单例模式的五种设计方案

时间:2023-03-25 22:52:05 Python

单例模式是一种常用的开发设计模式。它的主要目的是确保只有一个实例对象存在。也就是说,当你想让一个类具有单一的功能时,你只需要一个实例对象就可以完成任务,可以使用单例模式来节省内存资源。比如在我们的web开发项目中,经常需要做的一个功能就是使用单例模式开发短信验证码功能。我们平时使用的通讯产品都是发送短信验证码的,所以我们只需要使用一个实例对象就可以完成发送短信的功能。1.模块实现了单例模式。大家应该都知道模块的导入只能被导入的程序执行一次,多次导入也只会执行一次。那么我们可以说这个模块是天然的单例模式。因此,我们只需要在一个模块中定义相关的函数和数据,然后导入到其他模块中,就可以达到单例模式的效果。single.py···classSingleton(object):deffoo(self):passprint(1)singleton=Singleton()fromsingletonimportsingletonfromsingletonimportsingleton执行结果:12.使用装饰器实现单例模式装饰器I作用相信大家都知道了,我们可以在我们其他的函数或者类中添加一个函数,那么我们也可以给类写一个逻辑,让类只能生成一个实例对象。defSingleton(cls):_instance={}defsingleton(*args,**kargs):如果cls不在_instance:_instance[cls]=cls(*args,**kargs)return_instance[cls]returnsingleton@SingletonclassA(object):a=1def__init__(self,x=0):self.x=xa1=A(2)a2=A(3)print(a1)print(a2)执行结果:<__main__.Aobjectat0x0000027432DABE48><__main__.Aobjectat0x0000027432DABE48>3.使用类方法实现单例模式当我们在实际类中直接创建实例对象时,它不是单例对象,所以需要在类中定义一个类方法类的编辑逻辑实现单例模式,主要思路是判断类是否有属性_instance,有则直接返回实例,没有则创建实例。classSingleton(object):def__init__(self,*args,**kwargs):pass@classmethoddefget_instance(cls,*args,**kwargs):如果不是hasattr(Singleton,'_instance'):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instances1=Singleton.get_instance()s2=Singleton.get_instance()print(s1)print(s2)执行结果:<__main__.Singletonobjectat0x0000017B0359B550><__main__.Singletonobjectat0x0000017B0359B550>但是这样实现单例模式是有隐患的。当我们添加多个线程创建实例对象时,我们的执行速度足够快,没有任何影响。当执行速度不够快时,一个线程创建实例然后获取_instance属性进行判断,其他线程也可能同时获取_instance属性,发现没有实例,所以这两个线程会创建同时创建一个实例,这将导致创建多个线程。一个实例的现象,我们的单例模式自然会失效。比如这里我们直接加上这个休息时间来演示慢执行。importthreadingimporttimeclassSingleton(object):def__init__(self,*args,**kwargs):time.sleep(1)@classmethoddefget_instance(cls,*args,**kwargs):如果不是hasattr(Singleton,'_instance'):Singleton._instance=Singleton(*args,**kwargs)返回Singleton._instancedeftask(arg):obj=Singleton.get_instance(arg)print(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()执行结果:<__main__.Singleton对象在0x000001774942A518><__main__.Singleton对象在0x00000177495FA160><__main__.Singleton对象在0x00000177495F1EFSing0><__main__.Singleton__0x00000177495E5940><__main__.Singletonobjectat0x00000177495AC0F0><__main__.Singletonobjectat0x00000177495E58D0><__main__.Singletonobjectat0x00000177495A6FD0><__main__.Singletonobjectat0x00000177495F1D68><__main__.Singletonobjectat0x00000177496060B8><__main__.Singletonobjectat0x0000017749606240>现在我们有没有发现这个实例对象换了不同的ip?这就是我们刚才提到的原因,那么我们如何解决这个问题呢?这里我们可以通过加线程锁来达到效果。我们在添加线程锁后,给这个_instance加上这个锁,这样每个县用完后都会释放,然后下一个线程可以拿到,继续运行程序,这样就不会造成属性同时获取值并直接创建不同实例的情况。importthreadingimporttimeclassSingleton(object):_instance_lock=threading.Lock()def__init__(self,*args,**kwargs):time.sleep(1)@classmethoddefget_instance(cls,*args,**kwargs):如果nothasattr(Singleton,'_instance'):withSingleton._instance_lock:如果不是hasattr(Singleton,'_instance'):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instancedeftask(arg):obj=Singleton.get_instance(arg)print(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()执行结果:<__main__.Singletonobjectat0x000001EC8FAB9550><__main__。singleton对象在0x000001ec8fab9550><__main__。singleton对象at0x000001ec8fab9550><__main__.singleton对象at0x000001ec8fab9550><____________________________________________。jectat0x000001EC8FAB9550><__main__.Singletonobjectat0x000001EC8FAB9550><__main__.Singletonobjectat0x000001EC8FAB9550><__main__.Singletonobjectat0x000001EC8FAB9550>我们都知道我们都知道单例模式要通过new__方法来实现使用__new__4方法,如果我们重写类的__new__方法,是不是可以直接把可以创建多个实例的情况改写成只创建一个实例对象呢?答案是肯定的,我们可以在重写__new__方法的时候加上判断。如果有实例对象,那么直接返回创建的对象即可,这样就可以实现我们的效果了importthreadingclassSingleton(object):_instance_lock=threading.Lock()def__init__(self,*args,**kwargs):passdef__new__(cls,*args,**kwargs):如果不是hasattr(cls,'_instance'):withSingleton._instance_lock:ifnothasattr(cls,'_instance'):Singleton._instance=super().__new__(cls)returnSingleton._instancedeftask(arg):obj=Singleton()print(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()result:<__main__.Singletonobjectat0x00000111C88B9588>NoneNoneNoneNoneNoneNoneNoneNoneNone5.使用元类实现Python的单例模式元类是控制类创建的类。这样的话,我们是不是可以在创建类的时候通过元类来创建我们的类,让它只能生成一个实例对象呢?importthreadingclassSingletonType(type):_instance_lock=threading.Lock()def__call__(cls,*args,**kwargs):如果不是hasattr(cls,"_instance"):withSingletonType._instance_lock:如果不是hasattr(cls,"_我nstance"):cls._instance=super(SingletonType,cls).__call__(*args,**kwargs)返回cls._instanceclassFoo(metaclass=SingletonType):def__init__(self,name):self.name=nameobj1=Foo('name')obj2=Foo('name')print(id(obj1))print(id(obj2))执行结果:<__main__.Fooobjectat0x000001E97FC14400><__main__.Fooobjectat0x000001E97FC14400>Ok,soHere给大家介绍了几种创建单例模式的方法,总的来说,不管有用没用,我们在面试的时候假装x还是有用的,你掌握了吗?