Chapter6使用一等函数实现设计模式[toc]设计模式每个设计模式都围绕着以下三个问题展开:1.Why?即为什么要使用这种设计模式,使用这种模式之前存在什么样的问题?2.这是什么?通过Python语言实现本设计模式,解决Why中提到的问题。3.如何使用?了解了why之后,我们就基本明白在什么情况下使用这种模式了,不过这里我们会细化使用场景,说明这种模式的局限性和优缺点。本篇我们先来看看单例模式。单例模式是设计模式中逻辑最通俗易懂的模式。简单到只需要一句话就可以理解,即保证只有一个对象实例的模式。问题的关键在于它并不像看起来那么简单。但是让我们先讨论一下为什么需要这种模式。为什么我们先看看单例模式的使用场景,再分析为什么需要单例模式。Python的logger是单例模式,而用于logging的Windows资源管理器是单例模式的线程池,数据库连接池等资源池一般也使用单例模式的网站计数器从这些使用场景,我们可以总结出单例模式是以下几种情况需要:1.当每个实例都会占用资源,实例初始化会影响性能,这时候可以考虑使用单例模式。它给我们带来的好处是只有一个实例占用资源,并且只初始化一次;2、当有同步需求时,可以使用一个实例进行同步控制,比如共享文件(如日志文件)的控制,计数器的同步控制等。这种情况下,有只有一个实例,所以不用担心同步问题。当然,全部使用单例模式的前提是我们确实可以用一个实例来解决要解决的问题,而不用使用多个实例。如果每个实例都需要维护自己的状态,单例模式肯定不适合这种情况。适用的。下面我们就来看看如何使用Python来实现单例模式。最初的想法是什么很简单,实现如下::单例。__instance=object.__new__(cls,*args,**kwd)returnSingleton.__instances1=Singleton()s2=Singleton()print(s1)print(s2)ifs1==s2:print("True")否则:print("False")的结果是:<__main__.Singletonobjectat0x000002176FE884A8><__main__.Singletonobjectat0x000002176FE884A8>True如何使用在Python中,我们可以通过多种方式实现单例模式:使用模块使用__new__Using装饰器(decorators)和使用元类(metaclass)1.使用模块其实Python模块是天生的单例模式,因为模块第一次导入时,会生成一个.pyc文件,而当它被导入为第二次,将直接加载.pyc文件,而不会再次执行模块代码。因此,我们只需要在一个模块中定义相关的函数和数据,就可以得到一个单例对象。如果我们真的想要一个单例类,我们可以考虑这样做:#mysingleton.pyclassMy_Singleton(object):deffoo(self):passmy_singleton=My_Singleton()#将上面的代码保存在文件mysingleton.py中,然后像这样使用this:frommysingletonimportmy_singletonmy_singleton.foo()2.使用__new__方法为了让类只有一个实例,我们可以使用new来控制实例的创建过程,代码如下:classSingleton(object):_instance=Nonedef__new__(cls,*args,**kw):如果不是cls._instance:cls._instance=super(Singleton,cls).__new__(cls,*args,**kw)returncls._instanceclassMyClass(Singleton):a=1one=MyClass()two=MyClass()print(one==two)print(oneistwo)print(id(one),id(two))运行结果:TrueTrue248385644315224838564431523。使用我们知道的装饰器,decorate装饰器可以动态修改类或函数的功能。这里,我们也可以使用装饰器来装饰一个类,使其只生成一个实例,代码如下:fromfunctoolsimportwrapsdefsingleton(cls):instances={}@wraps(cls)defgetinstance(*args,**kw):如果cls不在实例中:instances[cls]=cls(*args,**kw)returninstances[cls]returngetinstance@singletonclassMyClass(object):a=1one=MyClass()two=MyClass()print(one==two)上面我们定义了一个装饰器单例,它返回一个内部函数getinstance,它会判断一个类是否在字典实例中,如果不在,则cls作为键,cls(args,*kw)作为值存储在instances中,否则直接返回instances[cls]。4.使用元类元类(metaclass)可以控制类的创建过程。它主要做了三件事:拦截类的创建,修改类的定义,返回修改后的类。使用元类实现单例模式的代码如下:]=super(Singleton,cls).__call__(*args,**kwargs)returncls._instances[cls]##Python2#classMyClass(object):#__metaclass__=Singleton#Python3classMyClass(metaclass=Singleton):passone=复制代码MyClass()two=MyClass()print(one==two)
