单例模式

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

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

装饰器

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

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

  1. def singleton(cls):
  2. instance = cls()
  3. instance.__call__ = lambda: instance
  4. return instance
  5. # Sample use
  6. @singleton
  7. class Highlander:
  8. x = 100
  9. # Of course you can have any attributes or methods you like.
  10. highlander = Highlander()
  11. another_highlander = Highlander()
  12. assert id(highlander) == id(another_highlander)

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

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

  1. def singleton(cls, *args, **kw):
  2. instances = {}
  3. def _singleton():
  4. if cls not in instances:
  5. instances[cls] = cls(*args, **kw)
  6. return instances[cls]
  7. return _singleton
  8. @singleton
  9. class MyClass(object):
  10. a = 1
  11. def __init__(self, x=0):
  12. self.x = x
  13. one = MyClass()
  14. two = MyClass()
  15. assert id(one) == id(two)

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

metaclass

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

  1. class Singleton(type):
  2. def __init__(cls, name, bases, dict):
  3. super(Singleton, cls).__init__(name, bases, dict)
  4. cls._instance = None
  5. def __call__(cls, *args, **kw):
  6. if cls._instance is None:
  7. cls._instance = super(Singleton, cls).__call__(*args, **kw)
  8. return cls._instance
  9. class MyClass(object):
  10. __metaclass__ = Singleton
  11. one = MyClass()
  12. two = MyClass()
  13. assert id(one) == id(two)

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

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

参考链接:Creating a singleton in python