zoukankan      html  css  js  c++  java
  • Python和Singleton (单件)模式

    我知道的一种在 python  Singleton mode 的实现如下:

    class Foo: pass

    def instance():

        global inst

        try:

            inst

        except:

            inst = Foo ()

        return inst

    该实现的优点就是简单和直观,但缺点也同样明显:

    1. 需要客户代码显式知道一个叫 instance() 的方法来创建该类的对象;
    2. 在并发环境下这种实现并不可靠;

     

     2 点是相当严重的一个缺陷,如果你用了上面的代码,那只能祈祷不要有 1 个以上的实例出现(虽然几率较低,但还是有可能),否则就会出现稀奇古怪的问题。

    一个稍微好些实现如下:

    class Singleton(object):   

        objs  = {}

        def __new__(cls, *args, **kv):

            if cls in cls.objs:

                return cls.objs[cls]

            cls.objs[cls] = object.__new__(cls)

    这个实现解决了第一个缺点,那些只需要一个实例的类想实现 Singleton mode ,只要从 Singleton 类继承即可,无论在代码的哪里实例化该类,都只存在该类的一个实例。

     

    为了解决第 2 个缺点,一个进化的版本出现了,如下:

    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()

    是不是看着眼熟,对了,这就是在 Singleton mode 中经典的双检查锁机制,该机制确保了在并发环境下 Singleton mode 的正确实现。

     

    到此为止,上面提到的 2 个缺点都被进化后的代码解决了,看上去已经很完美了,但是故事到此还没有结束,不知道你是否看出来改进后的代码还有什么问题吗?

     

    再继续之前,先介绍关于 __new__  __init__ 的基础知识, Python 的经典类和新式类都支持 __init__ 函数,但只有新式类支持 __new__ 函数,在一个新式类创建过程中, Python 解释器会先调用该类的 __new__ 函数 创建 实例,然后在调用 __init__ 函数 初始化 这个实例,如果这些函数不存在,就会调用 Python默认提供的版本,但如果用户提供了这些函数的实现,就会调用用户实现的版本。

     

    上面改进后的代码也存在 2 个问题:

    • 如果用户提供了自定义版本的 __new__ 函数,会覆盖或者干扰到 Singleton 类中 __new__ 的执行,但是这种情况出现的概率极小,因为很少有用户会 定制类实例 的创建过程;
    • 如果用户提供了自定义版本的 __init__ 函数,那么每次实例化该类的时候, __init__ 都会被调用到,这显然是不应该的, __init__ 只应该在创建实例的时候被调用一次;

     

    为了解决 __init__ 被多次调用的问题,一个更高级(同时也更复杂)的版本如下:

    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

     

    看到这里,你可能会想:一件简单的事情,有必要搞的那么复杂么?

    我的回答是:根据情况而定。

    • 如果你的运行环境不存在并发的情况,而且客户代码对额外的工作量(记住 instance() 这个函数)不在意的话,本文最开始的那段代码是最适合的;
    • 即使存在并发环境,但客户代码对额外的工作量(除了记住每个实例化函数,还要记得在同一个地方调用,并且要保证调用顺序)还是不太在意的话,那就在程序启动阶段初始化所有的单件实例,本文最开始的那段代码还是很合适的;
    • 如果有并发存在,并且客户很懒(懒是程序员的一种美德),不愿意记住太多和业务无关的东西,也不愿意费神集中初始化单件,并且还要保证初始化顺序,而且集中初始化在某些情况下(如有插件的系统中)是不可能的,那么还是使用最后这段复杂的代码吧。

     

    有得必有失

    简单的实现,代码逻辑清晰,维护量小,修改也很简单,但是应用环境受限(上面前 2 点所述),并且一些初始化工作交由客户来完成(调用 instance 函数),在小系统中这不是问题,但在一个大系统中,这会变成一个很明显的负担(特别是在实例化函数命名不统一的时候)。

     

    复杂的实现,所有创建操作在一处完成,不对环境作假设,不给客户带来任何负担,像普通类一样使用,最重要的是将单件创建(基类)和业务代码(继承类)分开,划分清晰;缺点是代码复杂,不好维护和修改,需要一定的 Python 高级语言特性。

  • 相关阅读:
    rabbitmq channel参数详解
    java中反射知识点总结
    SpringBoot的ApplicationRunner
    ServletContextInitializer添加 servlet filter listener
    如何在Job中获取 IOC applicationcontext
    QRCode.js:使用 JavaScript 生成微信二维码
    SpringBoot整合Quartz
    java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet解决
    javascript简介
    mysql索引
  • 原文地址:https://www.cnblogs.com/tqsummer/p/1943315.html
Copyright © 2011-2022 走看看