单例模式的实现方式
将类实例绑定到类变量上
class singleton(object):
_instance = none
def __new__(cls, *args):
if not isinstance(cls._instance, cls):
cls._instance = super(singleton, cls).__new__(cls, *args)
return cls._instance
但是子类在继承后可以重写__new__以失去单例特性
class d(singleton):
def __new__(cls, *args):
return super(d, cls).__new__(cls, *args)
使用装饰器实现
def singleton(_cls):
inst = {}
def getinstance(*args, **kwargs):
if _cls not in inst:
inst[_cls] = _cls(*args, **kwargs)
return inst[_cls]
return getinstance
@singleton
class myclass(object):
pass
问题是这样装饰以后返回的不是类而是函数,当然你可以singleton里定义一个类来解决问题,但这样就显得很麻烦了
使用__metaclass__,这个方式最推荐
class singleton(type):
_inst = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._inst:
cls._inst[cls] = super(singleton, cls).__call__(*args)
return cls._inst[cls]
class myclass(object):
__metaclass__ = singleton
tornado中的单例模式运用
来看看tornado.ioloop中的单例模式:
class ioloop(object):
@staticmethod
def instance():
“””returns a global `ioloop` instance.
most applications have a single, global `ioloop` running on the
main thread. use this method to get this instance from
another thread. to get the current thread’s `ioloop`, use `current()`.
“””
if not hasattr(ioloop, “_instance”):
with ioloop._instance_lock:
if not hasattr(ioloop, “_instance”):
# new instance after double check
ioloop._instance = ioloop()
return ioloop._instance
为什么这里要double check?来看个这里面简单的单例模式,先来看看代码:
class singleton(object):
@staticmathod
def instance():
if not hasattr(singleton, ‘_instance’):
singleton._instance = singleton()
return singleton._instance
在 python 里,可以在真正的构造函数__new__里做文章:
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
这种情况看似还不错,但是不能保证在多线程的环境下仍然好用,看图:
出现了多线程之后,这明显就是行不通的。
1.上锁使线程同步
上锁后的代码:
import threading
class singleton(object):
_instance_lock = threading.lock()
@staticmethod
def instance():
with singleton._instance_lock:
if not hasattr(singleton, ‘_instance’):
singleton._instance = singleton()
return singleton._instance
这里确实是解决了多线程的情况,但是我们只有实例化的时候需要上锁,其它时候singleton._instance已经存在了,不需要锁了,但是这时候其它要获得singleton实例的线程还是必须等待,锁的存在明显降低了效率,有性能损耗。
2.全局变量
在 java/c++ 这些语言里还可以利用全局变量的方式解决上面那种加锁(同步)带来的问题:
class singleton {
private static singleton instance = new singleton();
private singleton() {}
public static singleton getinstance() {
return instance;
}
}
在 python 里就是这样了:
class singleton(object):
@staticmethod
def instance():
return _g_singleton
_g_singleton = singleton()
# def get_instance():
# return _g_singleton
但是如果这个类所占的资源较多的话,还没有用这个实例就已经存在了,是非常不划算的,python 代码也略显丑陋……
所以出现了像tornado.ioloop.instance()那样的double check的单例模式了。在多线程的情况下,既没有同步(加锁)带来的性能下降,也没有全局变量直接实例化带来的资源浪费。
3.装饰器
如果使用装饰器,那么将会是这样:
import functools
def singleton(cls):
”’ use class as singleton. ”’
cls.__new_original__ = cls.__new__
@functools.wraps(cls.__new__)
def singleton_new(cls, *args, **kw):
it = cls.__dict__.get(‘__it__’)
if it is not none:
return it
cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
it.__init_original__(*args, **kw)
return it
cls.__new__ = singleton_new
cls.__init_original__ = cls.__init__
cls.__init__ = object.__init__
return cls
#
# sample use:
#
@singleton
class foo:
def __new__(cls):
cls.x = 10
return object.__new__(cls)
def __init__(self):
assert self.x == 10
self.x = 15
assert foo().x == 15
foo().x = 20
assert foo().x == 20
def singleton(cls):
instance = cls()
instance.__call__ = lambda: instance
return instance
#
# sample use
#
@singleton
class highlander:
x = 100
# of course you can have any attributes or methods you like.
highlander() is highlander() is highlander #=> true
id(highlander()) == id(highlander) #=> true
highlander().x == highlander.x == 100 #=> true
highlander.x = 50
highlander().x == highlander.x == 50 #=> true