设计模式中,最简单的一个就是 “单例模式”, 那么首先,就实现一下单例模式。
那么根据个人的理解,很快就写出第一版。
# -*- coding: utf-8 -*- class Singleton(object): # 定义静态变量实例 __singleton = None def __init__(self): pass @staticmethod def get_instance(): if Singleton.__singleton is None: Singleton.__singleton = Singleton() return Singleton.__singleton
测试一下:
if __name__ == "__main__": instance1 = Singleton.get_instance() instance2 = Singleton.get_instance() print id(instance1) print id(instance2)
liutrumpdeMacBook-Air:singleton trump$ python Singleton
4419778640
4419778640
看起来运行良好。但是其实,这里面有2个问题.
1. 这里类方法getinstance()用于获取单例,但是类本身也可以实例化,这样的方式其实并不符合单例模式的要求。
if __name__ == "__main__": instance1 = Singleton.get_instance() instance2 = Singleton.get_instance() instance3 = Singleton() print id(instance1) print id(instance2) print id(instance3) 执行结果: liutrumpdeMacBook-Air:singleton trump$ python Singleton 4461824080 4461824080 4461824144
在c#或java的设计模式中,我们通常是通过私有化类的构造函数来杀死类本身的繁殖能力
然而python并没有访问限定强制约束, 那么怎么办呢?
这个后续再说.
但是这样做也有好处,代码简单,大家约定好这样子调用就行了。
但是最好在类的命名上也体现了出来这是一个单例类.
2. 这个单例类并不是线程安全的.
比如我写了如下的测试代码来测试它的线程安全性.
def test_singleton_in_thread(): print id(Singleton.get_instance()) if __name__ == "__main__": idx = 0 while 1: MyThread(test_singleton_in_thread, []).start() idx += 1 if idx > 0X100: break
很快,就发现这确实不是线程安全的….
关于问题1. 我们换个思路, 来谈一谈python里面的构造函数.(其实python里面并没有构造函数个概念,⊙﹏⊙, 叫习惯了而已)
python 里的__init__(self) 函数,之前一直被我认为是python类的构造函数 __del__(self), 一直被我认为是类的析构函数…
其实,这是不对滴.
这时候__new__(self)就要登场了.
我们看一下官方的介绍.
http://docs.python.org/2/reference/datamodel.html#object.new
这么鬼长,其实告诉我们了一个道理.
new是一个类方法,会创建对象时调用。而init方法是在创建完对象后调用,对当前对象的实例做一些一些初始化,无返回值。如果重写了new而在new里面没有调用init或者没有返回实例,那么init将不起作用。
我擦,类方法,又是什么鬼….
好吧,继续查下资料.
http://www.cnblogs.com/2gua/archive/2012/09/03/2668125.html
静态方法:无法访问类属性、实例属性,相当于一个相对独立的方法,跟类其实没什么关系,换个角度来讲,其实就是放在一个类的作用域里的函数而已。
类成员方法:可以访问类属性,无法访问实例属性。 类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 了解了这些姿势以后,我们可以尝试通过改造类的__new__方法来给类进行计划生育了.
尝试了写了一下.
然而报一个递归溢出……
查了一下. 借鉴了一下
http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python/31887#31887
# -*- coding: utf-8 -*- class Singleton(object): # 定义静态变量实例 __instance = None def __init__(self): pass def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls.__instance if __name__ == "__main__": instance1 = Singleton() instance2 = Singleton() print id(instance1) print id(instance2)
liutrumpdeMacBook-Air:singleton trump$ python Singleton3.py
4544985488
4544985488
这里不是很懂super的用法. 查了一下文档.
看起来是调用了object类的 __new__方法来构造出了我们需要的类.(类似于c#里的反射???,不知道python的解释器是如何实现的)
总之看起来是靠谱的。耶耶耶。
同样的, 这种写法依然不是线程安全的.
关于问题2.
为了保证在多线程下线程安全性。
我们在写单例模式时候, 通常使用双重检查锁定来检测实例是否存在。
为什么用double check, 请自行思考…
实现:
# -*- coding: utf-8 -*- from MyThread import * import threading Lock = threading.Lock() class Singleton(object): # 定义静态变量实例 __instance = None def __init__(self): pass def __new__(cls, *args, **kwargs): if not cls.__instance: try: Lock.acquire() # double check if not cls.__instance: cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs) finally: Lock.release() return cls.__instance def test_singleton_in_thread(): print id(Singleton()) if __name__ == "__main__": idx = 0 while 1: MyThread(test_singleton_in_thread, []).start() idx += 1 if idx > 0X100: break
运行结果:
证明是线程安全的. 欧耶.
上面的代码在单例模式中被称作,懒汉式单例.
还有一种称之为饿汉式单例.
遗憾的是,python下是没有办法实现的。
饿汉,太饿了, 一开始就把实例构造出来…是不是很形象.
贴个c#的代码,供参考.
解释: 声明一个私有static 成员实例并直接调用类默认静态构造函数实例化.
然后安插共有静态方法返回该实例.
使用CLR的静态成员只能在静态构造函数中构造并且只能构造一次的特性来实现了单例.
特别的直观和美观, 也保证了线程安全.
使用起来很方便.
但也有个弊端就是需要在类加载的时候就把实例给初始化出来.
当这个实例非常大或者构造很耗时的话此时的性能就会有影响.
通常情况,这种写法是使用最多的写法(反正我写的话肯定不想使用double check…)
class Singleton { private static Singleton instance = new EagerSingleton(); private Singleton() { } public static Singleton GetInstance() { return instance; } }
总结:
本文介绍了使用python来实现不能约束构造实例只能通过规约指定方法来实现的单例模式,并由此引申控制类的__new__函数来
约束类构造的实例。
本文并且讨论了多线程下的double check 的单例模式的实现。
本文并且讨论了单例模式的懒汉式实现以及饿汉式实现.
好,第一个最简单的设计模式的python实现就到这里。
to be continued.