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中tell()方法的使用详解
May 24 Python
python连接数据库的方法
Oct 19 Python
对python中执行DOS命令的3种方法总结
May 12 Python
tensorflow 输出权重到csv或txt的实例
Jun 14 Python
Python实现登陆文件验证方法
Oct 06 Python
Django框架实现的简单分页功能示例
Dec 04 Python
python安装gdal的两种方法
Oct 29 Python
tensorflow之变量初始化(tf.Variable)使用详解
Feb 06 Python
Django使用Celery加redis执行异步任务的实例内容
Feb 20 Python
Python3 requests模块如何模仿浏览器及代理
Jun 15 Python
tensorboard 可视化之localhost:6006不显示的解决方案
May 22 Python
理解python中装饰器的作用
Jul 21 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 5.0对象模型深度探索之类的静态成员
2008/03/27 PHP
PHP冒泡排序算法代码详细解读
2011/07/17 PHP
php 邮件发送问题解决
2014/03/22 PHP
PHP 数组基本操作小结(推荐)
2016/06/13 PHP
为jQuery增加join方法的实现代码
2010/11/28 Javascript
js判断屏幕分辨率的代码
2013/07/16 Javascript
整理AngularJS中的一些常用指令
2015/06/16 Javascript
学习javascript文件加载优化
2016/02/19 Javascript
基于jQuery插件实现点击小图显示大图效果
2016/05/11 Javascript
H5用户注册表单页 注册模态框!
2016/09/17 Javascript
WEB前端实现裁剪上传图片功能
2016/10/17 Javascript
JavaScript中关于iframe滚动条的去除和保留
2016/11/17 Javascript
jQuery实现用户信息表格的添加和删除功能
2017/09/12 jQuery
Vue+webpack项目基础配置教程
2018/02/12 Javascript
详解Vue.js项目API、Router配置拆分实践
2018/03/16 Javascript
Python中字典(dict)和列表(list)的排序方法实例
2014/06/16 Python
python字符串替换的2种方法
2014/11/30 Python
python删除列表中重复记录的方法
2015/04/28 Python
Python pickle模块用法实例分析
2015/05/27 Python
多版本Python共存的配置方法
2017/05/22 Python
pycharm 将django中多个app放到同个文件夹apps的处理方法
2018/05/30 Python
Python中的单行、多行、中文注释方法
2018/07/19 Python
django页面跳转问题及注意事项
2019/07/18 Python
python操作cfg配置文件方式
2019/12/22 Python
CSS3区域模块region相关编写示例
2015/08/28 HTML / CSS
css3进阶之less实现星空动画的示例代码
2019/09/10 HTML / CSS
大学自主招生自荐信
2013/12/16 职场文书
家长给幼儿园的表扬信
2014/01/09 职场文书
新三好学生主要事迹
2014/01/23 职场文书
运动会开幕式解说词
2014/02/05 职场文书
超市开业庆典策划方案
2014/05/14 职场文书
解除劳动合同协议书范本
2014/09/13 职场文书
田径运动会通讯稿
2014/09/13 职场文书
python Tkinter的简单入门教程
2021/04/11 Python
python保存大型 .mat 数据文件报错超出 IO 限制的操作
2021/05/10 Python
在SQL Server中使用 Try Catch 处理异常的示例详解
2022/07/15 SQL Server