python单例模式应用场景,python3单例模式
本系列文章旨在用通俗易懂的语言清晰地解释软件项目中最常见的设计模式,并通过Python实现。每个设计模式都围绕以下三个问题:
为什么?也就是为什么要用这种设计模式?使用这种模式之前存在什么样的问题?这是什么?这个设计模式是用Python语言实现的,用来解决Why中提到的问题。怎么用?理解为什么我们基本知道什么时候使用这种模式,但是这里我们会细化使用场景,解释模式的局限性和优缺点。在本文中,让我们先来看看单例模式。它是单例模式设计模式中最简单、最容易理解的模式。简单到只用一句话就能理解,就是“只保证一个对象实例的模式”。问题的关键在于没有想象的那么简单。但是我们先来讨论一下为什么需要这个模型。
为什么我们先看单例模式的使用场景,然后分析为什么需要单例模式。
Python的logger是singleton模式,用来记录Windows的资源管理器是singleton模式线程池,数据库连接池等资源池一般使用singleton模式网站计数器。从这些使用场景中,我们可以总结出以下需要单体模式的情况:
当每个实例都会占用资源,并且实例的初始化会影响性能时,此时可以考虑使用singleton模式。它给我们带来的好处是只有一个实例占用资源,只需要初始化一次;当需要同步时,一个实例可以用于同步控制,例如共享文件(如日志文件)的控制和计数器的同步控制。在这种情况下,只有一个实例,所以不需要担心同步问题。当然,使用singleton模式的前提是我们可以用一个实例解决问题,而不用多个实例。如果每个实例都需要维护自己的状态,那么单实例模式在这种情况下肯定不适用。接下来,让我们看看如何使用Python来实现单例模式。
最初的想法很简单,是这样实现的:
class singleton(object):_ _ instance=nonedef _ _ new _ _(CLS,* args,* * kwargs): #这里不能使用__init__,因为__init__是生成实例后调用的ifcls。_ _实例为none: cls。_ _ instance=super (singleton,cls)。_ _ new _ _ (cls,* args,* * kwargs)返回cls。_ _ instances 1=singleton()S2=singleton()prints 1 prints 2打印结果如下:
_ _ main _ _。0x7F3580db110 _ _ main _处的单例对象。0x7F3580db110处的Singleton对象可以看出,该对象创建了两次,结果返回的是同一个对象实例。让我们的例子更接近真实的使用场景。
类Singleton(object):_ _ instance=None def _ _ new _ _(cls,*args,**kwargs):如果cls。_ _实例为None: cls。__instance=super( Singleton,cls)。__new__(cls,*args,**kwargs)返回cls。__instance def __init__(self,Status _ number): self。status _ number=status _ numbers 1=singleton(2)S2=singleton(5)prints 1 prints 2 prints 1。Status _ numberprints2。Status _ number这里我们使用_init_方法。这是打印结果。可见真的只有一个实例,共享实例的变量。
_ _ Main _ _。0x7F5116865490 _ _ Main _ _处的单例对象。0x7F511686549055处的Singleton对象但是,这个例子中有一个问题我们没有解决,就是多线程的问题。当多个线程同时初始化对象时,很有可能会同时判断__instance为None,从而进入初始化实例的代码。所以为了解决这个问题,我们必须通过同步锁来解决这个问题。下面的例子来自肖睿。
导入线程尝试:从同步导入make _ synchronized except导入错误:def make _ synchronized(func):导入线程func .__lock__=线程Lock() def synced_func(*args,**kws): with func .__lock__: return func(*args,* * kws)return synced _ func类Singleton(object):instance=None @ make _ synchronized def _ _ new _ _(cls,*args,**kwargs):如果cls.instance为None: cls.instance=object .__new__(cls,*args,* * kwargs)返回cls。instance def _ _ init _ _(self):self。博客=肖睿。cc def go(self):passdef worker():e=Singleton()print id(e)e . go()def test():E1=Singleton()E2=Singleton()E1。博客=123打印E1。博客打印E2。博客打印id(E1)if _ _ name _ _= _ _ main _ _ :test()task=[]for one in range(30):t=1 thread(target=worker)task。在task:one中为one追加(t)。start()用于任务中的一个:一个。加入()至此我们的单例模式实现代码已经接近完美了,不过我们是否可以更简单地使用单例模式呢?答案是有的,接下来就看看如何更简单地使用单例模式。
怎么用在计算机编程语言的官方网站给了两个例子是用装饰符来修饰类,从而使得类变成了单例模式,使得我们可以通过更加简单的方式去实现单例模式
例子:(这里只给出一个例子,因为更简单,另外一个大家可以看官网一个
def singleton(cls): instance=cls()实例. call _ _=lambda:instance返回instance # #示例使用# @ singleton类Highlander:x=100 #当然你可以有任何你喜欢的属性或者方法。汉兰达()是汉兰达()是汉兰达#=Trueid(汉兰达())==id(汉兰达)#=TrueHighlander().x==汉兰达。x==100 #=真正的汉兰达。x=50汉兰达().x==汉兰达. x==50 #=真这里简单解释下:
在定义汉兰达级的时候已经执行完所有一个装饰器中的代码,得到了一个实例,所以这之后所有对汉兰达的调用实际上是在调用情况的_呼叫_方法。我们通过希腊字母的第11个函数定义了_呼叫_方法让它始终返回实例,因此汉兰达()和汉兰达都返回情况同时由于在类定义代码执行时就已经创建了实例,所以后续不论是多线程还是单线程,在调用汉兰达时都是在调用情况的_呼叫_方法,也就无需同步了。
最后我想说的是这种方法简直碉堡了~~~
附上我用于多线程的测试代码导入线程定义单例(cls):instance=cls()实例. call _ _=lambda:instance返回instance @ singleton类Highlander:x=100 #当然你可以有任何你喜欢的属性或者方法。def worker():HL=汉兰达()HL。x=1打印HL打印HL。xdef main():threads=[]for _ in xrange(50):t=threading .线程(目标=工作线程)线程。append(t)for t in threads:t . start()for t in threads:t . join()if _ _ name _ _= _ _ main _ _ :main()这里的代码有一点小问题,就是在打印的时候有可能x属性已经被别的线程一了,所以有可能导致同一个数打印多次,而有的数没有打印,但是不影响最终x属性的结果,所以当所有线程结束之后,属性x最终的值是可以保证正确的。
作者:极客
链接:http://www。金淑。第二季第二集
來源:简书
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。