最近在看《head first design mode》,提到经典的单例模式的Java实现:
public class SingleTon {
private static SingleTon uniqueInstance;
private SingleTon() {
}
public static SingleTon getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new SingleTon();
}
return uniqueInstance;
}
}
设计思路是:
- 声明一个静态变量
uniqueInstance,用来存放将实例化的对象; - 将构造器声明为
private,这样实例化只能在由类本身发起; - 提供一个静态工厂方法
getInstance,所有外部类都只能通过这个方法进行实例化。在实例化时只要判断一下实例是否已经创建,如果没创建则实例化一个并赋值给uniqueInstance, 最后放回uniqueInstance即可。
注: 这个实现并未考虑多线程的问题,仍然存在多线程创建多个实例的可能性。解决方法有三种:1. 通过synchronize方法将工厂方法声明为同步操作;2. 声明静态变量时直接初始化;3. 利用volatile+synchronize关键字,使用双重锁检查机制解决。有兴趣的同学可以直接google具体实现和利弊点, 这里篇幅限制不展开了。
python中能否参考这个思路来实现呢?看如下代码:
class SingleTon(object):
_instance = None
@classmethod
def get_instance(cls, *args, **kwargs):
if not cls._instance:
cls._instance = SingleTon()
return cls._instance
if __name__ == '__main__':
instance_a = SingleTon.get_instance()
instance_b = SingleTon.get_instance()
instance_c = SingleTon()
instance_d = SingleTon()
print(instance_a is instance_b)
print(instance_c is instance_d)
这段代码的最终输出如下:
True
Flase
可以看到,使用类方法进行实例化时达到了目的;但是,由于 python中并没有访问权限控制机制(而是依赖良好的规范约束,全靠自觉),依然存在调用方直接进行实例化的可能性,从而破坏了单例的事实。
那么如何解决这个问题呢?
重写__new__方法实现
在解决这个问题前需要了解一下python的两个方法: __new__ 和__init__:
__new__:这是一个类方法,用于创建实例(只创建对象,不做初始化),并返回创建的实例对象。
__init__:这是一个实例方法,用来做实例初始化的。该方法总是传入一个self对象,而self对象就是__new__方法创建的。因此,__new__方法总是先于__init__被调用。
既然问题的关键就在于控制实例对象的创建,我们很自然的想到重写__new__方法来达到目的, 具体实现如下:
class ConcreteClass(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
self.config = {}
self.items = []
if __name__ == '__main__':
instance_a = ConcreteClass()
print(id(instance_a.config))
instance_b = ConcreteClass()
print(id(instance_b.config))
print(instance_a is instance_b)
得到输出:
2253469934464
2253470263360
True
不过上面的方法问题在于,虽然__new__方法的_instance对象不会重新创建,但是实例化对象的第二步__init__方法还是会执行,相当于对象会反复执行__init__方法!导致一些奇怪的问题,比如上面的config对象其实发生了变化,因此,该方法有效的前提是__init__方法里面啥都不做!
元类实现
正确的方法是利用元类(metaclass), 所谓元类就是用来创建类的类,元类常见于ORM实现。我们知道函数其实也是一个对象,函数的调用实际上是调用了函数对象的__call__方法。既然类是元类的一个实例,那么元类的__call__方法实际就是类实例化方法(回忆一下obj=SomeClass()语法)。因此,我们可以通过指定元类的__call__方法来达到这个目的,如下:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class ConcreteClass(metaclass=Singleton):
def __init__(self):
self.config = {}
self.items = []
if __name__ == '__main__':
instance_a = ConcreteClass()
print(id(instance_a.config))
instance_b = ConcreteClass()
print(id(instance_b.config))
print(instance_a is instance_b)
得到结果依然是:
1827331186880
1827331186880
True