Python 中的单例模式

June 09, 2015

单例模式,是一种常用的软件设计模式,单例对象的类必须保证只有一个实例存在。

之前自己土鳖的实现:

_instance = Singleton()

class Singleton(object):
    @staticmethod
    def get_instance():
        return _instance

上面的实现缺点很明显,无论有没有调用 Singletonget_instance 方法,_instance 对象总会初始化。改进也很简单:

class Singleton(object):
    _instance = None

    @staticmethod
    def get_instance():
        if Singleton._instance is None:
            Singleton._instance = Singleton()
        return Singleton._instance

用类的静态变量替换全局变量,就只会在使用时才初始化单例对象。对于 CPython 来说,由于 GIL 的限制,单个进程同一时刻至多有一个线程运行,所以不需要担心 get_instance 函数第 2 行执行时产生多个单例。如果考虑其他可能发生并发的 Python 实现,可以使用下面的加锁版本:

import threading

class Singleton(object):
    _instance = None
    _instance_lock = threading.Lock()

    @staticmethod
    def get_instance():
        with Singleton._instance_lock:
            if Singleton._instance is None:
                Singleton._instance = Singleton()
        return Singleton._instance

上面的实现在每次执行 get_instance 时都会加锁,还不够好,因此就有了下面的”双重检查加锁”版本:

class Singleton(object):
    _instance = None
    _instance_lock = threading.Lock()

    @staticmethod
    def get_instance():
        if Singleton._instance is None:
            with Singleton._instance_lock:
                if Singleton._instance is None:
                    Singleton._instance = Singleton()
        return Singleton._instance

每次写单例要写那么多代码很麻烦呀,怎么办呢?有装饰器来帮忙:

def singleton(cls):
    instances = {}
    def wrap(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrap

这个装饰起没有考虑多线程并发问题,不过在日常的应用中(CPython)应该足够用了


参考文献:

单例模式 - 维基百科

Tornado: ioloop.py


Profile logo

Written by Alvie Zhang

If you're going through hell, keep going