在最开始接触设计方法的时候,我们经常会接触到单例模式,它是23种GOF模式中最简单,也是最经常出现的一种设计模式,也是面试官最常爱考的一种模式。单例模式是一种创建型模式,它提供了一种创建对象的方式,确保只有单个对象被创建。这个设计模式主要目的是想在整个系统中只能出现类的一个实例,即一个类只有一个对象。

什么是单例模式?

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

— Wikipedia

实现单例模式的思路

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

— Wikipedia

如何实现单例模式?

Python 中有四种方式可以实现单例模式,分别是

  • __new__() 方法
  • __metaclass__
  • 装饰器
  • import

1. __new__() 方法

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

### Useage

class Test(Singleton):
    pass

test1 = Test()
test2 = Test()

id(test1)
id(test2)

2. __metaclass__ 元类

metaclass,元类,是Python的高级语法,我们可以通过它来实现单例模式。

## 这个版本只在 Python3 中起作用
class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Test(metaclass=Singleton):
    pass    
## works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Test(Singleton):
    pass

test1 = Test()
test2 = Test()

3. 装饰器

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    pass

4. import

在Python中模块在被导入时会被执行一次,我们利用此特性来实现单例。

# singleton.py
class Singleton(object):
    def hello(self):
        print('hello')
    pass

instance = Singleton()

# usage
from singleton import instance

instance.hello()

最佳实践

使用单例模式的注意事项

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

— Wikipedia

推荐使用 metaclass 的方式去实现单例模式。为什么呢?

首先, A metaclass is the class of a class. 这句话的含义也就是一个类其实是其元类的实例。在上面的示例中,Test这个类其实是 Singleton 的一个实例。当我们调用 Test() 时,Python会首先询问 Test 的元类,也就是 Singleton, 该如何做,允许提前创建实例。引用一张图来说明这个过程:

图片引用自 Understanding Python metaclasses | ionel’s codelog

另外我们经常会在网上看到通过共享属性的方式去实现单例,比如:

class Singleton(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Singleton, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class Test(Singleton):
    pass

a = Test()
b = Test()

当我们在 a 上操作之后虽然也会影响到 b,但是我们需要注意的是 id(a) != id(b) 也就是说 a 和 b 并是同一个实例。

参考链接