• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

Python和Singleton (单件)模式实现代码

python 水墨上仙 1869次浏览

Python和Singleton单件)模式实现代码

我知道的一种在&nbsppython&nbsp中&nbspSingleton&nbspmode&nbsp的实现如下:

class Foo: pass
def instance():
    global inst
    try:
        inst
    except:
        inst = Foo ()
    return inst

该实现的优点就是简单和直观,但缺点也同样明显:
需要客户代码显式知道一个叫&nbspinstance()&nbsp的方法来创建该类的对象;
在并发环境下这种实现并不可靠;
&nbsp
第&nbsp2&nbsp点是相当严重的一个缺陷,如果你用了上面的代码,那只能祈祷不要有&nbsp1&nbsp个以上的实例出现(虽然几率较低,但还是有可能),否则就会出现稀奇古怪的问题。
一个稍微好些实现如下:

class Singleton(object):   
    objs  = {}
    def __new__(cls, *args, **kv):
        if cls in cls.objs:
            return cls.objs[cls]
        cls.objs[cls] = object.__new__(cls)

这个实现解决了第一个缺点,那些只需要一个实例的类想实现&nbspSingleton&nbspmode&nbsp,只要从&nbspSingleton&nbsp类继承即可,无论在代码的哪里实例化该类,都只存在该类的一个实例。
&nbsp
为了解决第&nbsp2&nbsp个缺点,一个进化的版本出现了,如下:

class Singleton(object):
    objs  = {}
    objs_locker =  threading.Lock()
    def __new__(cls, *args, **kv):
        if cls in cls.objs:
            return cls.objs[cls]
        cls.objs_locker.acquire()
        try:
            if cls in cls.objs: ## double check locking
                return cls.objs[cls]
            cls.objs[cls] = object.__new__(cls)
        finally:
            cls.objs_locker.release()

是不是看着眼熟,对了,这就是在&nbspSingleton&nbspmode&nbsp中经典的双检查锁机制,该机制确保了在并发环境下&nbspSingleton&nbspmode&nbsp的正确实现。
&nbsp
到此为止,上面提到的&nbsp2&nbsp个缺点都被进化后的代码解决了,看上去已经很完美了,但是故事到此还没有结束,不知道你是否看出来改进后的代码还有什么问题吗?
&nbsp
再继续之前,先介绍关于&nbsp__new__&nbsp和&nbsp__init__&nbsp的基础知识,&nbspPython&nbsp的经典类和新式类都支持&nbsp__init__&nbsp函数,但只有新式类支持&nbsp__new__&nbsp函数,在一个新式类创建过程中,&nbspPython&nbsp解释器会先调用该类的&nbsp__new__&nbsp函数&nbsp创建&nbsp实例,然后在调用&nbsp__init__&nbsp函数&nbsp初始化&nbsp这个实例,如果这些函数不存在,就会调用&nbspPython默认提供的版本,但如果用户提供了这些函数的实现,就会调用用户实现的版本。
&nbsp
上面改进后的代码也存在&nbsp2&nbsp个问题:
如果用户提供了自定义版本的&nbsp__new__&nbsp函数,会覆盖或者干扰到&nbspSingleton&nbsp类中&nbsp__new__&nbsp的执行,但是这种情况出现的概率极小,因为很少有用户会&nbsp定制类实例&nbsp的创建过程;
如果用户提供了自定义版本的&nbsp__init__&nbsp函数,那么每次实例化该类的时候,&nbsp__init__&nbsp都会被调用到,这显然是不应该的,&nbsp__init__&nbsp只应该在创建实例的时候被调用一次;
&nbsp
为了解决&nbsp__init__&nbsp被多次调用的问题,一个更高级(同时也更复杂)的版本如下:

class Singleton(object):
   
    objs  = {}
    objs_locker =  threading.Lock()
    def __new__(cls, *args, **kv):
        if cls in cls.objs:
            return cls.objs[cls]['obj']
        cls.objs_locker.acquire()
        try:
            if cls in cls.objs: ## double check locking
                return cls.objs[cls]['obj']
            obj = object.__new__(cls)
            cls.objs[cls] = {'obj': obj, 'init': False}
            setattr(cls, '__init__', cls.decorate_init(cls.__init__))
        finally:
            cls.objs_locker.release()
   
    @classmethod
    def decorate_init(cls, fn):
        def init_wrap(*args):
            if not cls.objs[cls]['init']:
                fn(*args)
                cls.objs[cls]['init'] = True
            return
        return init_wrap
 

看到这里,你可能会想:一件简单的事情,有必要搞的那么复杂么?
我的回答是:根据情况而定。
如果你的运行环境不存在并发的情况,而且客户代码对额外的工作量(记住&nbspinstance()&nbsp这个函数)不在意的话,本文最开始的那段代码是最适合的;
即使存在并发环境,但客户代码对额外的工作量(除了记住每个实例化函数,还要记得在同一个地方调用,并且要保证调用顺序)还是不太在意的话,那就在程序启动阶段初始化所有的单件实例,本文最开始的那段代码还是很合适的;
如果有并发存在,并且客户很懒(懒是程序员的一种美德),不愿意记住太多和业务无关的东西,也不愿意费神集中初始化单件,并且还要保证初始化顺序,而且集中初始化在某些情况下(如有插件的系统中)是不可能的,那么还是使用最后这段复杂的代码吧。
&nbsp
有得必有失
简单的实现,代码逻辑清晰,维护量小,修改也很简单,但是应用环境受限(上面前&nbsp2&nbsp点所述),并且一些初始化工作交由客户来完成(调用&nbspinstance&nbsp函数),在小系统中这不是问题,但在一个大系统中,这会变成一个很明显的负担(特别是在实例化函数命名不统一的时候)。
&nbsp
复杂的实现,所有创建操作在一处完成,不对环境作假设,不给客户带来任何负担,像普通类一样使用,最重要的是将单件创建(基类)和业务代码(继承类)分开,划分清晰;缺点是代码复杂,不好维护和修改,需要一定的&nbspPython&nbsp高级语言特性。

 


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明Python和Singleton (单件)模式实现代码
喜欢 (1)
加载中……