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__
函数可以改变返回实例的特点做到这一点。