视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Python设计模式中单例模式的实现及在Tornado中的应用
2020-11-27 14:35:36 责编:小采
文档
 单例模式的实现方式
将类实例绑定到类变量上

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

下载本文
显示全文
专题