Python中实现单例的N种方法

0x00 前言

单例是一种很常见的设计模式,在Python中不同的实现方法差异也比较大。这里介绍一些不同的实现方法。

0x01 基本法

class MyClass(object):
    _instance = None

    @staticmethod
    def get_instance():
        if not MyClass._instance:
            MyClass._instance = MyClass()
        return MyClass._instance

inst = MyClass.get_instance()

这种方法是最简单的实现方法,但是需要使用者主动调用get_instance方法来获取实例,如果写成inst = MyClass()的话,就不会起到单例的作用了。

0x02 重载new大法

为了解决上面的问题,可以通过重载__new__方法来实现。

class MyClass(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(MyClass, cls).__new__(cls, *args, **kwargs)
            cls._instance.initialize(*args, **kwargs)
        return cls._instance

    def initialize(self, *args, **kwargs):
        pass

inst = MyClass()

类在实例化的时候,会先调用__new__方法,在这里可以修改返回的实例。但是,这种方法有一个问题,就是实例化的时候一定会调用__init__方法,因此会出现重复初始化的问题。这里改为使用initialize方法进行初始化,也就是说,使用者需要避免使用__init__进行初始化。

但是这种方法对用户不是透明的,体验上不是很好。

0x03 元类法

元类是一种特殊的类,它继承自type,拥有创造类的能力,因此成为元类


class SingletonMetaClass(type):

    def __init__(cls, *args, **kwargs):
        super(SingletonMetaClass, cls).__init__(*args, **kwargs)
        print('SingletonMetaClass __init__')

    def __call__(cls, *args, **kwargs):
        print('SingletonMetaClass __call__')
        if not hasattr(cls, '_instance'):
            cls._instance = super(SingletonMetaClass, cls).__call__(*args, **kwargs)
        print('SingletonMetaClass __call__ return')
        return cls._instance

class MyClass(object, metaclass=SingletonMetaClass):

    def __new__(cls, *args, **kwargs):
        print('MyClass __new__')
        return super(MyClass, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('MyClass __init__')

inst = MyClass()

输出如下内容:

SingletonMetaClass __init__
SingletonMetaClass __call__
MyClass __new__
MyClass __init__
SingletonMetaClass __call__ return

可以看出,正是在元类的__call__中,创造了MyClass这个类。

这种方法一般用来是没什么问题的,但是有些情况下会报:TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases这个错误。这是因为基类中也使用了元类的原因,此时需要保证元类的继承关系,以避免元类冲突。

0x04 装饰器法

装饰器是一种常用的动态修改函数行为的方法,因此也可以用于实现单例。

class Singleton(object):
    '''singleton decorator
    '''

    def __init__(self, cls):
        self.__instance = None
        self.__cls = cls

    def __call__(self, *args, **kwargs):
        if not self.__instance:
            self.__instance = self.__cls(*args, **kwargs)
        return self.__instance

@Singleton
class MyClass(object):
    pass

相比其它方法,这种方法缺点更少,使用也更加灵活,不需要修改类的实现。

0x05 总结

为了实现通用的单例逻辑,主要思路就是修改类的实例化过程。

__new__方法、元类法装饰器法都是通过在实例化之前判断是否已经实例化,从而返回对应的实例,差别只是在于实现逻辑位于实例化的不同阶段。元类法装饰器法是通过在类实例化之前判断是否已经进行过实例化;而__new__方法是在已经进入实例化过程,但是尚未进到__init__过程,利用__new__函数可以改变返回实例的特点做到这一点。

分享