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和Ruby中each循环引用变量问题(一个隐秘BUG?)
Jun 04 Python
Python使用pygame模块编写俄罗斯方块游戏的代码实例
Dec 08 Python
在Mac OS上使用mod_wsgi连接Python与Apache服务器
Dec 24 Python
Python编程之序列操作实例详解
Jul 22 Python
Python实现的生成格雷码功能示例
Jan 24 Python
Python爬虫信息输入及页面的切换方法
May 11 Python
如何优雅地改进Django中的模板碎片缓存详解
Jul 04 Python
Python timer定时器两种常用方法解析
Jan 20 Python
使用Python实现Wake On Lan远程开机功能
Jan 22 Python
python如何实现DES加密
Sep 21 Python
基于注解实现 SpringBoot 接口防刷的方法
Mar 02 Python
Python打包为exe详细教程
May 18 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格式化工具Beautify PHP小小BUG
2008/04/24 PHP
php的ZipArchive类用法实例
2014/10/20 PHP
php利用gd库为图片添加水印
2016/11/09 PHP
PHP面向对象程序设计之类与反射API详解
2016/12/02 PHP
php实现留言板功能
2017/03/05 PHP
php实现文件管理与基础功能操作
2017/03/21 PHP
php上传excel表格并获取数据
2017/04/27 PHP
PHP闭包定义与使用简单示例
2018/04/13 PHP
一个简单的JS鼠标悬停特效具体方法
2013/06/17 Javascript
iframe窗口高度自适应的又一个巧妙实现思路
2014/04/04 Javascript
JavaScript数据类型详解
2015/04/01 Javascript
JavaScript获取URL汇总
2015/06/08 Javascript
Javascript将数值转换为金额格式(分隔千分位和自动增加小数点)
2016/06/22 Javascript
JavaScript作用域示例详解
2016/07/07 Javascript
使用jQuery ajaxupload插件实现无刷新上传文件
2017/04/23 jQuery
JS switch判断 三目运算 while 及 属性操作代码
2017/09/03 Javascript
利用jquery和BootStrap实现动态滚动条效果
2018/12/03 jQuery
微信小程序停止其他视频播放当前视频的实例代码
2019/12/25 Javascript
剖析Python的Twisted框架的核心特性
2016/05/25 Python
Python解决N阶台阶走法问题的方法分析
2017/12/28 Python
在python中利用GDAL对tif文件进行读写的方法
2018/11/29 Python
Django Docker容器化部署之Django-Docker本地部署
2019/10/09 Python
Python中低维数组填充高维数组的实现
2019/12/02 Python
python给图像加上mask,并提取mask区域实例
2020/01/19 Python
selenium如何定位span元素的实现
2021/01/13 Python
基于css3 animate制作绚丽的动画效果
2015/11/24 HTML / CSS
美国领先的户外服装与装备用品店:Moosejaw
2016/08/25 全球购物
高中毕业自我鉴定
2013/12/22 职场文书
生产厂厂长岗位职责
2013/12/25 职场文书
部门活动策划方案
2014/08/16 职场文书
护理医院见习报告
2014/11/03 职场文书
感动中国何玥观后感
2015/06/02 职场文书
2016五一劳动节慰问信
2015/11/30 职场文书
Pytorch使用shuffle打乱数据的操作
2021/05/20 Python
JavaGUI模仿QQ聊天功能完整版
2021/07/04 Java/Android
MySQL数据库完全卸载的方法
2022/03/03 MySQL