Python设计模式中单例模式的实现及在Tornado中的应用


Posted in Python onMarch 02, 2016

单例模式的实现方式
将类实例绑定到类变量上

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

这种情况看似还不错,但是不能保证在多线程的环境下仍然好用,看图:

Python设计模式中单例模式的实现及在Tornado中的应用

出现了多线程之后,这明显就是行不通的。

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
Python 相关文章推荐
python使用内存zipfile对象在内存中打包文件示例
Apr 30 Python
python使用心得之获得github代码库列表
Jun 25 Python
Python2.x和3.x下maketrans与translate函数使用上的不同
Apr 13 Python
Python使用面向对象方式创建线程实现12306售票系统
Dec 24 Python
Python的Flask框架应用调用Redis队列数据的方法
Jun 06 Python
Python中的日期时间处理详解
Nov 17 Python
python 切换root 执行命令的方法
Jan 19 Python
python如何获取当前文件夹下所有文件名详解
Jan 25 Python
Pycharm简单使用教程(入门小结)
Jul 04 Python
Python中zip()函数的解释和可视化(实例详解)
Feb 16 Python
numpy的Fancy Indexing和array比较详解
Jun 11 Python
Django Session和Cookie分别实现记住用户登录状态操作
Jul 02 Python
Python使用设计模式中的责任链模式与迭代器模式的示例
Mar 02 #Python
详解Python设计模式编程中观察者模式与策略模式的运用
Mar 02 #Python
Python设计模式编程中解释器模式的简单程序示例分享
Mar 02 #Python
分析Python中设计模式之Decorator装饰器模式的要点
Mar 02 #Python
实例解析Python设计模式编程之桥接模式的运用
Mar 02 #Python
Python随机生成带特殊字符的密码
Mar 02 #Python
Python设计模式编程中Adapter适配器模式的使用实例
Mar 02 #Python
You might like
PHP 字符串正则替换函数preg_replace使用说明
2011/07/15 PHP
PHP多个文件上传到服务器实例
2014/10/29 PHP
php将文本文件转换csv输出的方法
2014/12/31 PHP
php页面缓存方法小结
2015/01/10 PHP
PHP文件缓存类示例分享
2015/01/30 PHP
php 使用ActiveMQ发送消息,与处理消息操作示例
2020/02/23 PHP
extjs 3.31 TreeGrid实现静态页面加载json到TreeGrid里面
2013/04/02 Javascript
jQuery Validate 验证,校验规则写在控件中的具体实例
2014/02/27 Javascript
Node.js中使用事件发射器模式实现事件绑定详解
2014/08/15 Javascript
JavaScript中继承用法实例分析
2015/05/16 Javascript
基于jquery实现全屏滚动效果
2015/11/26 Javascript
javascript垃圾收集机制的原理分析
2016/12/08 Javascript
Bootstrap导航条鼠标悬停下拉菜单
2017/01/04 Javascript
ES6新特性六:promise对象实例详解
2017/04/21 Javascript
javaScript封装的各种写法
2017/08/14 Javascript
简述Angular 5 快速入门
2017/11/04 Javascript
vue中,在本地缓存中读写数据的方法
2018/09/21 Javascript
解决vuejs 使用value in list 循环遍历数组出现警告的问题
2018/09/26 Javascript
从0到1构建vueSSR项目之路由的构建
2019/03/07 Javascript
利用Vue-draggable组件实现Vue项目中表格内容的拖拽排序
2019/06/07 Javascript
jQuery事件委托代码实践详解
2019/06/21 jQuery
VsCode里的Vue模板的实现
2020/08/12 Javascript
[04:36]DOTA2国际邀请赛 ti3精彩集锦
2013/08/19 DOTA
[01:32]TI奖金增速竟因它再创新高!DOTA2勇士令状不朽珍藏Ⅰ饰品欣赏
2018/05/18 DOTA
Django objects.all()、objects.get()与objects.filter()之间的区别介绍
2017/06/12 Python
Python实现socket非阻塞通讯功能示例
2019/11/06 Python
工程师岗位职责
2013/11/08 职场文书
授权委托书怎么写
2014/04/03 职场文书
舞蹈教育学专业求职信
2014/06/29 职场文书
公司领导班子对照检查存在问题整改措施
2014/10/02 职场文书
2014党的群众路线教育实践活动总结材料
2014/10/31 职场文书
2014年结对帮扶工作总结
2014/12/17 职场文书
金正昆讲礼仪观后感
2015/06/11 职场文书
学生安全责任协议书
2016/03/22 职场文书
Html分层的box-shadow效果的示例代码
2021/03/30 HTML / CSS
Python基于Opencv识别两张相似图片
2021/04/25 Python