Python中实现单例的N种方法

0x00 前言

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

0x01 基本法

  1. class MyClass(object):
  2. _instance = None
  3. @staticmethod
  4. def get_instance():
  5. if not MyClass._instance:
  6. MyClass._instance = MyClass()
  7. return MyClass._instance
  8. inst = MyClass.get_instance()
COPY

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

0x02 重载new大法

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

  1. class MyClass(object):
  2. _instance = None
  3. def __new__(cls, *args, **kwargs):
  4. if not cls._instance:
  5. cls._instance = super(MyClass, cls).__new__(cls, *args, **kwargs)
  6. cls._instance.initialize(*args, **kwargs)
  7. return cls._instance
  8. def initialize(self, *args, **kwargs):
  9. pass
  10. inst = MyClass()
COPY

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

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

0x03 元类法

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

  1. class SingletonMetaClass(type):
  2. def __init__(cls, *args, **kwargs):
  3. super(SingletonMetaClass, cls).__init__(*args, **kwargs)
  4. print('SingletonMetaClass __init__')
  5. def __call__(cls, *args, **kwargs):
  6. print('SingletonMetaClass __call__')
  7. if not hasattr(cls, '_instance'):
  8. cls._instance = super(SingletonMetaClass, cls).__call__(*args, **kwargs)
  9. print('SingletonMetaClass __call__ return')
  10. return cls._instance
  11. class MyClass(object, metaclass=SingletonMetaClass):
  12. def __new__(cls, *args, **kwargs):
  13. print('MyClass __new__')
  14. return super(MyClass, cls).__new__(cls, *args, **kwargs)
  15. def __init__(self, *args, **kwargs):
  16. print('MyClass __init__')
  17. inst = MyClass()
COPY

输出如下内容:

  1. SingletonMetaClass __init__
  2. SingletonMetaClass __call__
  3. MyClass __new__
  4. MyClass __init__
  5. SingletonMetaClass __call__ return
COPY

可以看出,正是在元类的__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 装饰器法

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

  1. class Singleton(object):
  2. '''singleton decorator
  3. '''
  4. def __init__(self, cls):
  5. self.__instance = None
  6. self.__cls = cls
  7. def __call__(self, *args, **kwargs):
  8. if not self.__instance:
  9. self.__instance = self.__cls(*args, **kwargs)
  10. return self.__instance
  11. @Singleton
  12. class MyClass(object):
  13. pass
COPY

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

0x05 总结

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

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

分享
0 comments
Anonymous
Markdown is supported

Be the first guy leaving a comment!