单例模式
天然的的单例模式
其实,Python 的模块就是天然的单例模式。因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
但如何设计一个单例模式的类,这也就是本文的目的:
自定义单例类
装饰器方法
这个方法使用了装饰器维护了一个字典对象 instances,它的作用是缓存所有单例类,如果不存在则创建,反之直接返回(类似sys.modules)。
In [1]: def singleton(cls):
...: instances = dict()
...: def wrapper(*args, **kwargs):
...: if cls not in instances:
...: instances[cls] = cls(*args, **kwargs)
...: return instances[cls]
...: return wrapper
...:
...:
In [2]: @singleton
...: class Foo(object):
...: pass
...:
...:
In [3]: foo1 = Foo()
In [4]: foo2 = Foo()
In [5]: id(foo1)
Out[5]: 2396816959528
In [6]: id(foo2)
Out[6]: 2396816959528
基类方法
__new__
方法是真正用来实例化对象的方法,所以重写其__new__
方法就可以进行单例模式。
而单例模式最为关键的是存在直接返回,不存在则创建。所以将实例对象用一个变量引用住,以后每次创建时直接返回该对象。
In [9]: class Singleton(object):
...: def __new__(cls, *args, **kwargs):
...: if not hasattr(cls, '_instance'):
...: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
...: return cls._instance
...:
In [10]: class Foo(Singleton):
...: pass
...:
...:
In [11]: foo1 = Foo()
In [12]: foo2 = Foo()
In [13]: id(foo1)
Out[13]: 2396817286760
In [14]: id(foo2)
Out[14]: 2396817286760
元类方法
"Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't."
—Tim Peters
一个安全的多线程单例模式
由于单例模式总是会去判断实例是否被创建,在多线程的环境下,多个线程可能会拿到同一个结果,这样就无法实现单例。为此需要加锁。
In [1]: import threading
In [2]: def singleton_lock(func):
...: func.__lock__ = threading.Lock()
...: def lock_func(*args, **kwargs):
...: with func.__lock__:
...: return func(*args, **kwargs)
...: return lock_func
...:
...:
In [3]: class Singleton(object):
...: @singleton_lock
...: def __new__(cls, *args, **kwargs):
...: if cls.instance is None:
...: cls.instance = object.__new__(cls, *args, **kwargs)
...: return cls.instance
...: