单例模式

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时, 单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个 全局对象,这样有利于我们协调系统整体的行为。 --以上来自维基百科

从定义上来看,这会是一个很有用的避免冲突的设计模式,相当于把所有同样资源的调用 都交给了一个资源代理。那么 Python 中该如何实现这一模式呢?

装饰器

所有资源资源调用者都是同一个对象,我首先想到的就是装饰器,可以很方便的 给不同的对象增添相同的功能。

Python 官方 wiki 给出了一个非常优雅的实现:


def singleton(cls):
    instance = cls()
    instance.__call__ = lambda: instance
    return instance

# Sample use

@singleton
class Highlander:
    x = 100
    # Of course you can have any attributes or methods you like.


highlander = Highlander()
another_highlander = Highlander()
assert id(highlander) == id(another_highlander)

上面的代码定义了一个 singleton 装饰器,覆盖了类的 __call__ 方法, 该方法会在类创建的时候创建一个类的实例,并在之后类每次的实例化时总是返回这个实例对象。

当然,如果你希望只在需要的时候创建类的实例对象也有别的方法:


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

@singleton
class MyClass(object):
    a = 1
    def __init__(self, x=0):
        self.x = x

one = MyClass()
two = MyClass()

assert id(one) == id(two)

上面的代码中实现了这样一个装饰器:装饰器函数创建的时候会创建一个 instances 字典, 该字典用于保存被装饰器修改过的类的实例,在类初始化的时候首先判断是否存在其实例, 如果存在则直接返回,否则创建一个新的实例保存到 instances 字典中,并返回该实例。 (这段代码出自 cnblogs

metaclass

我自己对于不是很喜欢装饰器的实现,因为从语言逻辑上来看,我需要的是一个 有单例特性的类而不是为类添加单例限制。于是我又找到了基于 metaclass 的实现:


class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls._instance = None
    def __call__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__call__(*args, **kw)
        return cls._instance

class MyClass(object):
    __metaclass__ = Singleton

one = MyClass()
two = MyClass()

上面的代码在类的第一次实例之后将这个个实例作为其类变量保存,在之后调用类的构造函数的时候 都直接返回这个实例对象。

这个解决方案强化了类与其单例之间的内聚性。

参考链接:Creating a singleton in python