介绍

在开发的过程中,对于某个类,有时候整个软件只需要一个该类别的全局对象(例如全局配置信息类),此时我们就可以使用 单例模式(singleton pattern)。在单例模式中,单例类有且只有一个对象实例,当我们尝试获取该类的对象时,如果该对象实例已初始化则直接返回它,否则初始化该类的一个对象实例并返回。

优缺点如下:

  • (o) 在多线程的情况下要小心,最好加互斥锁(虽然影响效率但保证线程安全)。

实现

方式一:利用函数装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def singleton(cls):
    _cls_to_singleton = {}

    def wrapped_cls(*args, **kwargs):
        if cls not in _cls_to_singleton:
            _cls_to_singleton[cls] = cls(*args, **kwargs)
        return _cls_to_singleton[cls]

    return wrapped_cls

@singleton
class DemoClass(object):
    def __init__(self):
        pass

obj1 = DemoClass()
obj1 = DemoClass()
assert id(obj1) == id(obj1)

通过在装饰器函数内保存一个字典 _cls_to_singleton 保存从类别 cls 到单例对象 singleton 的映射实现。这个方法还有一些变种,例如将装饰器函数改成装饰器类等等。

方式二:利用 __new__() 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class DemoClass(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

obj1 = DemoClass()
obj1 = DemoClass()
assert id(obj1) == id(obj1)

通过操作 __new__ 来实现。这里必须要强调一下在 Python 中,__new__() 方法负责创建对象实例,而 __init__() 方法负责初始化实例。对于单例模式而言,我们需要的是「只存在一个对象实例」,而不需要「每次都重新初始化」,因此我们操纵 __new__ 来实现单例模式,其中的对象单例 _instance 是作为类变量存在。

方式三:metaclass

1
# TODO

在 Python 中,类(class)通过 __new__() 方法创造对象实例(instance),而元类(metaclass)通过 __metaclass__() 方法创造类(class)。所以我们可以通过操控元类来实现单例模式。