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 相关文章推荐
Flask SQLAlchemy一对一,一对多的使用方法实践
Feb 10 Python
Python中urllib2模块的8个使用细节分享
Jan 01 Python
详解在Python程序中自定义异常的方法
Oct 16 Python
Python随机生成带特殊字符的密码
Mar 02 Python
python实现简单socket通信的方法
Apr 19 Python
浅谈pyhton学习中出现的各种问题(新手必看)
May 17 Python
TensorFlow神经网络优化策略学习
Mar 09 Python
python 编码规范整理
May 05 Python
Sanic框架基于类的视图用法示例
Jul 18 Python
selenium+python截图不成功的解决方法
Jan 30 Python
Pytorch之view及view_as使用详解
Dec 31 Python
python和anaconda的区别
May 06 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
通过5个php实例细致说明传值与传引用的区别
2012/08/08 PHP
PHP下载生成的csv文件及问题总结
2015/08/06 PHP
深入浅析PHP无限极分类的案例教程
2016/05/09 PHP
thinkPHP框架乐观锁和悲观锁实例分析
2019/10/30 PHP
Mozilla中显示textarea中选择的文字
2006/09/07 Javascript
IE浏览器兼容Firefox的JS脚本的代码
2008/10/23 Javascript
JQuery 1.3.2以上版本中出现pareseerror错误的解决方法
2011/01/11 Javascript
JavaScript学习笔记之数组随机排序
2016/03/23 Javascript
基于js对象,操作属性、方法详解
2016/08/11 Javascript
javascript 定时器工作原理分析
2016/12/03 Javascript
如何给ss bash 写一个 WEB 端查看流量的页面
2017/03/23 Javascript
angularJs中$scope数据序列化的实例
2018/09/30 Javascript
微信小程序实现弹出层效果
2020/05/26 Javascript
怎样在vue项目下添加ESLint的方法
2019/05/16 Javascript
使用react context 实现vue插槽slot功能
2019/07/18 Javascript
jquery实现点击弹出对话框
2020/02/08 jQuery
利用原生JS实现欢乐水果机小游戏
2020/04/23 Javascript
js 获取扫码枪输入数据的方法
2020/06/10 Javascript
Vue如何实现验证码输入交互
2020/12/07 Vue.js
python 3.5实现检测路由器流量并写入txt的方法实例
2017/12/17 Python
Python logging管理不同级别log打印和存储实例
2018/01/19 Python
在python中获取div的文本内容并和想定结果进行对比详解
2019/01/02 Python
Python3按一定数据位数格式处理bin文件的方法
2019/01/24 Python
python使用BeautifulSoup与正则表达式爬取时光网不同地区top100电影并对比
2019/04/15 Python
python树的同构学习笔记
2019/09/14 Python
python与c语言的语法有哪些不一样的
2020/09/13 Python
python中最小二乘法详细讲解
2021/02/19 Python
英国奢侈品概念店:Base Blu
2019/05/16 全球购物
大学毕业后的十年规划
2014/01/07 职场文书
个人求职信范文分享
2014/01/31 职场文书
高中军训第一天感言
2014/03/06 职场文书
学生保证书范文
2014/04/28 职场文书
争先创优演讲稿
2014/09/15 职场文书
大学生国庆节65周年演讲稿范文
2014/09/25 职场文书
python实现图片九宫格分割的示例
2021/04/25 Python
SpringBoot SpringEL表达式的使用
2021/07/25 Java/Android