python的官方wiki给出了一个用修饰符实现的单例,如下:
def singleton(cls):
instance = cls()
instance.__call__ = lambda: instance
return instance
@singleton
class Highlander:
x = 100
本文主要就这个单例背后的原理展开剖析,其实这里最重要的结论是:Highlander已经是”Highlander”的实例了,为了说明这个事实,我们先看一些熟悉的东西:
class Highlander:
x = 100
if __name__ == '__main__':
h = Highlander()
print type(h)
print type(Highlander)
这里按常规方式定义了类Hightlander,输出如下:
<type 'classobj'>
<type 'instance'>
这说明Highlander是类,而 h 是 Hightlander的一个实例。但是将Hightlander用修饰符修饰后,代码如下:
def singleton(cls):
instance = cls()
instance.__call__ = lambda: instance
return instance
@singleton
class Highlander:
x = 100
if __name__ == '__main__':
h = Highlander()
print type(h)
print type(Highlander)
输出如下:
<type 'instance'>
<type 'instance'>
可见,Highlander已经确实成为实例了,即实例的名字与类的名字相同,而原来的类名被屏蔽了,那么 h = Highlander() 又是什么呢?h 实际上是 call的返回结果,我们可以再做一个实验:
def singleton(cls):
instance = cls()
instance.__call__ = lambda: 1
return instance
@singleton
class Highlander:
x = 100
if __name__ == '__main__':
h = Highlander()
print type(h)
print type(Highlander)
输出如下:
<type 'int'>
<type 'instance'>
可见,此时 h 就变成整型了,已经跟Handlander没有关系了。
这一切的关键在于修饰符到底是怎么工作的,简单点说,修饰符会有如下效果:
def fun1(f):
...
@fun1
def fun2():
...
等价于:
def fun1(f):
...
def fun2():
...
fun2 = fun1(fun2)
也就是说,fun2被定义了2次,第一次是原生的,第二次又被fun1定义了一下,所以最终的fun2是fun1的返回值。
现在回到本文,也就是说实现单例的方法等同如如下代码:
def singleton(cls):
instance = cls()
instance.__call__ = lambda: instance
return instance
class Highlander:
x = 100
Highlander = singleton(Highlander)
传入singleton的参数是类,然后第一步是生成了这个类的一个实例,第二步是定义了这个实例的__call__函数,第三步是将这个实例返回,singleton的返回值就是类Highlander的实例,而从此以后,类名Highlander就消失了(被屏蔽了),由此实现了单例。