Python 中类的单例实现

单例设计模式是应用开发过程中最简单的创建型的设计模式。类的单例指的是一个类只有一个实例,可以提供一个全局的访问节点,常常用于日志记录,数据库操作等等,它们的特点是资源一般只有一个,单一的访问实例可以避免同一资源的冲突。

Python中典型的单例的实现方式

#!/usr/bin/env python3
#coding:utf8

class Singleton(object):
    def __new__(cls, *arg, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance
    def __init__(self, v):
        self.v = v
        print("v=", self.v)

if __name__ == "__main__":
    s1 = Singleton(3)
    s2 = Singleton(2)
    print(s1 == s2)
    print("s1.v=", s1.v)

上述代码通过重写__new__方法实现了类的单一性,__new__是python中一个特别的魔法方法,它的一个参数cls代表要初始化的类,在类初始化的时候解释器自动提供,它直接返回一个初始化好的类,在上面的例子中,首先会去查找相应的类的实例_instance是否存在,如果不存在就调用类的初始化方法生成一个新的类,super方法会返回类的一个类的定义,通过调用__new__方法实现类的例化,例化完成后将实例保存在_instance中,从而实现类的单一实例。

通过元类的方式实现

元类是一个类的类,也就是说类实际上是元类的实例,通过元类,我们可以实现自定义类型的定义,比如对于MyClass的类,我们可以定义一个MyKls的元类来控制MyClass的行为。在python中创建类的对象的时候会调用__call__方法实现类的初始化,因此可以通过定义__call__方法实现对类初始化方式的控制。

class MetaSingleton(type):
    _instances = {}
    def __call__(cls, *args, **kw):
        if cls not in cls._instances:
            cls._instances[cls] = super(MetaSingleton, \
                    cls).__call__(*args, **kw)
            print("Create new class")
        return cls._instances[cls]

class Logger(metaclass=MetaSingleton):
    def __init__(self, src):
        self.src = src
        print("Logger init called:", self.src)

这也是一种实现类的单例的方式,不过这种方式和之前的方式有一些差别,通过元类__call__方法实现的单例,会直接劫持__new____init__方法,也就是类只有第一次初始化的时候会调用__init__方法,而第一种方法实际每次初始化的时候还会重新调用新的init方法,这就是两者之前的差异,不过一般在使用单例模式时候会显式的定义一个init方法,而不用自带的__init__方法,防止对内部的全局变量意外的修改。

装饰函数的实现方式

与元类重写__call__的实现方式类似,实际还有一种更为简洁的单例的实现方式,就是采用装饰函数的实现方式:

def single_instance(cls, *args, **kw):
    """
    A decorator to guaranteed a class only init once
    """
    instances = {}

    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return _singleton

通过元类的定义我们知道类的实例化就是相当于对类的一个调用,所以可以用一个装饰函数对这个调用加以修改从而实现对类的控制。上面的这种方式就是对类在调用进行修改,从而实现类只有一个实例的效果。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

发表新评论