单例模式(SingletonPattern)是一种常用的软件设计模式。这种模式的主要目的是确保某个类只存在一个实例。当您只想在整个系统中出现一个类的一个实例时,单例对象会派上用场。例如,一个服务器程序的配置信息保存在一个文件中,客户端通过一个AppConfig类来读取配置文件的信息。如果程序运行过程中有很多地方需要用到配置文件的内容,也就是说很多地方需要创建AppConfig对象的实例,这样就会导致系统中有多个AppConfig实例对象,会严重浪费内存。资源,特别是如果配置文件有很多内容。实际上,对于AppConfig这样的类,我们希望在程序运行过程中只有一个实例对象存在。在Python中,我们可以使用多种方法来实现单例模式:使用模块,使用装饰器,使用类,基于__new__方法实现,基于元类方法实现,下面详细介绍:使用模块其实,Python模块是天生的单例模式,因为模块第一次导入时会生成.pyc文件,第二次导入时直接加载.pyc文件,不会再次执行模块代码。因此,我们只需要在一个模块中定义相关的函数和数据,就可以得到一个单例对象。如果我们真的想要一个单例类,可以考虑这样做:这个文件在其他文件中,这个对象是单例对象frommysingletonimportsingletonusedecoratordefSingleton(cls):_instance={}def_singleton(*args,**kargs):ifclsnotin_instance:_instance[cls]=cls(*args,**kargs)return_instance[cls]返回_singleton@SingletonclassA(object):a=1def__init__(self,x=0):self.x=xa1=A(2)a2=A(3)使用类classSingleton(object):def__init__(self):pass@classmethoddefinstance(cls,*args,**kwargs):ifnothasattr(Singleton,"_instance"):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instance一般来说,大家认为这样就完成了单例模式,但是使用多线程的时候就会出现问题:classSingleton(object):def__init__(self):pass@classmethoddefinstance(cls,*args,**kwargs):如果不是hasattr(Singleton,"_instance"):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instanceimportthreadingdeftask(arg):obj=Singleton.instance()打印(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()程序执行后,打印结果如下:<__main__.Singletonobjectat0x02C933D0><__main__.Singletonobjectat0x02C933D0><__main__.Singletonobjectat0x02C933D0><__main__.0x02C933D0处的Singleton对象><__main__.0x02C933D0处的Singleton对象><__main__.0x02C933D0处的Singleton对象><__main__.0x02C933D0处的Singleton对象><__main__.0x02C933D0处的Singleton对象><__main__.SingletonC93.02__3.02__处的对象Singletonobjectat0x02C933D0>好像没有问题,是因为执行速度太快了,如果__init__方法里面有一些IO操作,就会发现问题接下来,我们通过time.sleep进行模拟。我们在上面的__init__方法中加入如下代码:def__init__(self):importtimetime.sleep(1)重新执行程序后,结果如下:<__main__.Singletonobjectat0x034A3410><__main__.Singleton0x034BB990处的对象><__main__.0x034BB910处的单例对象><__main__.0x034ADED0处的单例对象><__main__.0x034E6BD0处的单例对象><__main__.0x034E6C10处的单例对象><__main__.0x034E6B90处的单例对象><__main__。Singletonobjectat0x034BBA30><__main__.Singletonobjectat0x034F6B90><__main__.Singletonobjectat0x034E6A90>问题出现了!上述方式创建的单例不支持多线程。解决办法:锁定它!解锁部分并发执行,锁定部分串行执行。速度有所降低,但数据安全得到保证。importtimeimportthreadingclassSingleton(object):_instance_lock=threading.Lock()def__init__(self):time.sleep(1)@classmethoddefinstance(cls,*args,**kwargs):withSingleton._instance_lock:如果不是hasattr(Singleton,"_instance"):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instancedeftask(arg):obj=Singleton.instance()print(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()time.sleep(20)obj=Singleton.instance()print(obj)打印结果如下:<__main__.Singletonobjectat0x02D6B110><__main__.0x02D6B110处的单例对象><__main__.0x02D6B110处的单例对象><__main__.0x02D6B110处的单例对象><__main__.0x02D6B110处的单例对象><__main__.0x02D6B110处的单例对象><__main__><__main__.01x02D__main__6处的单例对象.Singleton对象位于0x02D6B110><__main__.Singleton对象at0x02D6B110><__main__.Singletonobjectat0x02D6B110>这个差不多,但是还有一个小问题,就是程序执行的时候,time.sleep(20)执行完之后,下面实例化对象的时候,已经是singletonExample模式了但是我们还是加了锁,不是很好,然后进行了一些优化,把实例方法改成下面这样:@classmethoddefinstance(cls,*args,**kwargs):ifnothasattr(Singleton,"_instance"):withSingleton._instance_lock:ifnothasattr(Singleton,"_instance"):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instance这样一个单例模式,可以支持多线程就完事了。+importtimeimportthreadingclassSingleton(object):_instance_lock=threading.Lock()def__init__(self):time.sleep(1)@classmethoddefinstance(cls,*args,**kwargs):如果不是hasattr(Singleton,"_instance"):withSingleton._instance_lock:ifnothasattr(Singleton,"_instance"):Singleton._instance=Singleton(*args,**kwargs)returnSingleton._instancedeftask(arg):obj=Singleton.instance()打印(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()time.sleep(20)obj=Singleton.instance()print(obj)这样实现的单例模式在使用的时候会有限制。以后一定要通过obj=Singleton.instance()来实例化。如果使用obj=Singleton(),这样得到的结果不是单例。基于__new__方法的实现通过上面的例子我们可以知道,我们在实现单例的时候,需要在内部加一把锁来保证线程安全。我们知道,当我们实例化一个对象时,首先会执行类的__new__方法(不写的时候默认调用object.__new__),实例化对象;然后我们执行类的__init__方法来处理对象的初始化,都可以以此为基础实现单例模式。importthreadingclassSingleton(object):_instance_lock=threading.Lock()def__init__(self):passdef__new__(cls,*args,**kwargs):如果不是hasattr(Singleton,"_instance"):withSingleton._instance_lock:如果不是hasattr(Singleton,"_instance"):Singleton._instance=object.__new__(cls)返回Singleton._instanceobj1=Singleton()obj2=Singleton()print(obj1,obj2)deftask(arg):obj=Singleton()print(obj)foriinrange(10):t=threading.Thread(target=task,args=[i,])t.start()打印结果如下:<__main__.Singletonobjectat0x038B33D0><__main__.0x038B33D0处的单例对象<__main__.0x038B33D0处的单例对象><__main__.0x038B33D0处的单例对象><__main__.0x038B33D0处的单例对象><__main__.0x038B33D0处的单例对象><__main__.0x038B33D0处的单例对象><__main__.0x038B33D0处的单例对象
