喵吉欧尼酱

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::
Python单例模式的实现方法

这里先对类里面的两个方法解释:
    __new__ :被调用的 class 作为其第一个参数,该函数的功能任务是返回类的一个新实例。
    __init__ :被调用的实例作为其第一个参数,它不返回任何东西;其职责是初始化实例。
    __call__ :是模拟类的调用,相当于重载了括号运算符。


### 方法1 ###
  #方法1,共享属性
    #所谓单例就是所有引用(实例、对象)拥有相同的状态(属性)和行为(方法)。但类判断时并不相同。
    #同一个类的所有实例天然拥有相同的行为(方法),
    #只需要保证同一个类的所有实例具有相同的状态(属性)即可
    #所有实例共享属性的最简单最直接的方法就是__dict__属性指向(引用)同一个字典(dict)
    class Borg(object):
        _state = {}
        def __new__(cls, *args, **kw):
            ob = super(Borg, cls).__new__(cls, *args, **kw)
            ob.__dict__ = cls._state
            return ob

    class MyClass2(Borg):
        a = 1

    one = MyClass2()
    two = MyClass2()

    #one和two是两个不同的对象,id, ==, is对比结果可看出
    two.a = 3
    print( one.a ) # 3
    print( id(one) ) # 28873680
    print( id(two) ) # 28873712
    print( one == two ) # False
    print( one is two ) # False
    #但是one和two具有相同的(同一个__dict__属性),见:
    print( id(one.__dict__) ) # 30104000
    print( id(two.__dict__) ) # 30104000

    '''
    结论:
    如果并不关心生成的实例是否具有同一id,而只关心其状态(属性)和行为方式(方法)时,可以用这方法。
    坑: 类实例不是同一个,类判断时并不相同。严格的来讲,这并非单例。
    '''


### 方法2 ###
  #方法2:使用装饰器(decorator)。
    #单例类本身根本不知道自己是单例的,因为他本身(自己的代码)并不是单例的
    def singleton(cls, *args, **kw):
        instances = {}
        def _singleton():
            if cls not in instances:
                instances[cls] = cls(*args, **kw)
            return instances[cls]
        return _singleton

    @singleton
    class MyClass(object):
        a = 1
        def __init__(self, x=0):
            self.x = x

    one = MyClass()
    two = MyClass()

    two.a = 3
    print( one.a ) # 3
    print( id(one) ) # 29660784
    print( id(two) ) # 29660784
    print( one == two ) # True
    print( one is two ) # True
    one.x = 1
    print( one.x ) # 1
    print( two.x ) # 1

    '''
    结论:
    从原理上来说,就是把一个类的进行定义偷换,换为一个函数:
        该函数接收调用者传入的初始化的参数,并检查缓存中是否已经有了该类的对象,
        如果没有该类的实例,则创建一个放入缓存并返回实例,(有关并发安全问题不在此分析)。
        看上去满足我们的要求,只需将该类的声明之上放上一个装饰器即可。
    坑: 类不能再次被继承。

    原因分析:类被装饰过之后,就会返回一个函数,而不是类本身,故在之后被继承的时候用做父类会报异常,因为这单例类本质上来说是一个method而非类。
    该缺陷使得类不能再次被继承,在代码书写上有很大的局限性,不爽!
    '''


### 方法3 ###
  #方法3,实现__new__方法,改写创建类的过程(并非 new 一个类实例的过程)
    #并在将一个类的实例绑定到类变量_instance上,
    #如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
    #如果cls._instance不为None,直接返回cls._instance
    class Singleton(object):
        # __new__ 会先于 __init__ 执行, 且首参数 cls 是 MyClass类 而不是 MyClass类实例。
        # __new__ 是一个类方法,在创建对象时调用。而 __init__ 方法是在创建完对象后,调用时对当前对象的实例做一些初始化,无返回值。
        # 如果重写了 __new__ 而在 __new__ 里面没有调用 __init__ 或者没有返回实例,那么 __init__ 将不起作用。
        def __new__(cls, *args, **kw):
            if not hasattr(cls, '_instance'):
                orig = super(Singleton, cls)
                cls._instance = orig.__new__(cls, *args, **kw)
            return cls._instance

    # 只要继承 Singleton 就可以实现单例, Singleton 可以有多个不同的单例子类且互不干扰。
    class MyClass(Singleton):
        a = 1
        def __init__(self):
            print('------init ------') # 用来判断执行了多少次 init 函数

    class MyClass2(MyClass):
        pass

    one = MyClass2() # 打印了一次 init
    two = MyClass2() # 又打印了一次 init

    two.a = 3
    print( one.a ) # 3
    #one和two完全相同,可以用id(), ==, is检测
    print( id(one) ) # 29097904
    print( id(two) ) # 29097904
    print( one == two ) # True
    print( one is two ) # True

    '''
    结论:
    从上面的测试结果可以看出:
        1. 可以被多重继承。
        2. 没有多次实例化对象。

    似乎满足我们的要求,但是致命缺陷:
        __init__ 方法被多次调用,坑来了,虽然多次创建出来的都是一个对象,但是该对象在我们每一次实例化的过程中都会被调用一次,与我们想象的只被调用一次差别有点大啊,不过感觉这个思路应该是对的。

    坑: 实例重复调用__init__(),习惯上我们的初始化代码都是放在这个方法里面的。
    '''


### 方法4 ###
  #方法4:使用__metaclass__(元类)的高级python用法。 注意只有 py2.x 可行, py3.x 不行。
    #本质上是方法2的升级(或者说高级)版
    class Singleton(type):
        # __init__ 会先于 __call__ 执行, 且首参数 cls 是 MyClass类 而不是 MyClass类实例(因为这是元类)。只在定义 MyClass类 时执行一次,后续不再执行。
        def __init__(cls, name, bases, dct):
            super(Singleton, cls).__init__(name, bases, dct)
            cls._instance = None

        # 每次 new MyClass类 时都会执行, 且首参数 cls 是 MyClass类 而不是 MyClass类实例(因为这是元类)。
        def __call__(cls, *args, **kw):
            if cls._instance is None:
                cls._instance = super(Singleton, cls).__call__(*args, **kw)
            return cls._instance

    class MyClass(object):
        __metaclass__ = Singleton
        def __init__(self):
            print('------init ------') # 用来判断执行了多少次 init 函数

    class MyClass2(MyClass):
        pass

    one = MyClass2() # 打印了一次 init
    two = MyClass2() # 不再打印 init

    two.a = 3
    print( one.a ) # 3
    print( id(one) ) # 31495472
    print( id(two) ) # 31495472
    print( one == two ) # True
    print( one is two ) # True

    '''
    结论:
    从上面的测试结果可以看出:
        1. 可以被多重继承。
        2. 没有多次实例化对象。
        3. __init__ 函数也只是调用过一次。
    '''




下面是非常规方法

### 使用模块 ###
    # python中的 模块module 在程序中只被加载一次,本身就是单例的。
    # 可以直接写一个模块,将你需要的方法和属性,写在模块中当做函数和模块作用域的全局变量即可,根本不需要写类。
    # 而且还有一些综合模块和类的优点的方法:
    class _singleton(object):
        class ConstError(TypeError):
            pass
        def __setattr__(self,name,value):
            if name in self.__dict__: # 这里是多次赋值时抛异常, 实用中不可能这样用。
                raise self.ConstError
            self.__dict__[name]=value
        def __delattr__(self,name):
            if name in self.__dict__:
                raise self.ConstError
            raise NameError
    import sys
    sys.modules[__name__]=_singleton()

    # python 并不会对 sys.modules 进行检查以确保他们是模块对象,我们利用这一点将模块绑定向一个类对象,而且以后都会绑定向同一个对象了。
    # 将代码存放在single.py中:

    >>> import single
    >>> single.a=1
    >>> single.a=2
    ConstError
    >>> del single.a
    ConstError


### 覆盖类名 ###
    class singleton(object):
        pass
    singleton=singleton()
    # 将名字singleton绑定到实例上,singleton就是它自己类的唯一对象了。
    # 但是这样也就不能再 new 这个对象,而是直接当成变量调用,感觉上有点怪。

 

posted on 2017-08-19 10:20  喵吉欧尼酱  阅读(300)  评论(0)    收藏  举报