使用 __new__ 实现 Python 的单例模式

使用 __new__ 实现 Python 的单例模式


Python 类的初始化

Python 类的初始化分为两步

  • 创建一个类的实例
  • 对实例中的变量进行初始化

我们常用的 __init__ 函数实际上是第二步,而第一步即是 __new__ 函数,一个 __new__ 函数默认实现为:

class Testclass(object):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

    def __init__(self, params):
        pass

这里 object.__new__(cls, *args, **kwargs) 返回的即 __init__ 中的实例,我们对上面代码进行修改,更直观的来看一下一个对象初始化的过程:

class Testclass(object):
    def __new__(cls, *args, **kwargs):
        print 'in __new__'
        print cls
        instance = object.__new__(cls, *args, **kwargs)
        print 'out __new__'
        return instance

    def __init__(self):
        print 'in __init__'
        print self
        print 'out __init__'

if __name__ == '__main__':
    t = Testclass()

测试结果:

in __new__

out __new__
in __init__
<__main__.Testclass object at 0x019AC2D0>
out __init__

从上面测试中我们可以看到,在 __new__ 中,cls 为构建的类,object.__new__ 返回一个类实例,然后该实例会通过 self 传递给 __init__,然后对实例对象进行初始化

构建单例模式

对以上过程有了一个认识后,我们就可以构建一个单件模式了。对于单件,当一个类已经创建了实例,后续就不再创建该类的实例,而是直接返回已创建的实例。基于此,我们对 __new__ 进行重写,如下:

class Test(object):
    __instance = None
    __firstinit = 1

    def __new__(cls, *args, **kwargs):
        if Test.__instance == None:
            Test.__instance = object.__new__(cls, *args, **kwargs)
        return Test.__instance

    def __init__(self):
        if not Test.__firstinit:
            return
        Test.__firstinit = 0

if __name__ == "__main__":
    a = Test()
    b = Test()
    print a
    print b

上例中我们将类的实例保存到一个类属性 __instance 中,一旦类属性不为 None,我们就不再调用 __new__,而是直接返回 __instance。另外为避免每次调用 Test() 都会执行一遍实例初始化,我们引入了一个 __firstinit 的类属性,执行结果:

<__main__.Test object at 0x019FC930>
<__main__.Test object at 0x019FC930>

我们可以看到 a 和 b 是相同的实例。

单例模式类的继承

在进行单例模式类继承时,需要注意,子类的 __new__ 在创建实例时,不能调用父类的 __new__,否则会直接通过判断父类的 __instance 属性,应该写成下面方式:

class TestChild(Test):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if TestChild.__instance == None:
            TestChild.__instance = object.__new__(cls, *args, **kwargs)
        return TestChild.__instance

有兴趣的朋友可以尝试把 object 改为 Test 试一下,创建出来的实例与 Test 的实例是相同的实例,这里就不再进行演示了

其它实现方式

当然除了,这种方式,还有使用装饰器,__metaclass__ 等方式来实现单例模式的,有兴趣的童鞋可以参考这篇博文
http://blog.csdn.net/karldoenitz/article/details/23467221

你可能感兴趣的:(Python)