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查找第k小元素代码分享
Dec 18 Python
python处理圆角图片、圆形图片的例子
Apr 25 Python
收藏整理的一些Python常用方法和技巧
May 18 Python
python 文件操作api(文件操作函数)
Aug 28 Python
Python基于scipy实现信号滤波功能
May 08 Python
python用match()函数爬数据方法详解
Jul 23 Python
在PyTorch中使用标签平滑正则化的问题
Apr 03 Python
Python unittest装饰器实现原理及代码
Sep 08 Python
Python猫眼电影最近上映的电影票房信息
Sep 18 Python
python time()的实例用法
Nov 03 Python
django使用多个数据库的方法实例
Mar 04 Python
Python Django模型详解
Oct 05 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
关于PHP5 Session生命周期介绍
2010/03/02 PHP
php 使用post,get的一种简洁方式
2010/04/25 PHP
php利用腾讯ip分享计划获取地理位置示例分享
2014/01/20 PHP
Codeigniter中mkdir创建目录遇到权限问题和解决方法
2014/07/25 PHP
Javascript 的addEventListener()及attachEvent()区别分析
2009/05/21 Javascript
jquery模拟按下回车实现代码
2011/09/20 Javascript
jQuery提交多个表单的小例子
2013/06/30 Javascript
jQuery动画效果-fadeIn fadeOut淡入浅出示例代码
2013/08/28 Javascript
getJSON调用后台json数据时函数被调用两次的原因猜想
2013/09/29 Javascript
浅谈页面装载js及性能分析方法
2014/12/09 Javascript
浅谈javascript的Array.prototype.slice.call
2015/08/31 Javascript
谈谈Jquery中的children find 的区别有哪些
2015/10/19 Javascript
jquery实现无刷新验证码的简单实例
2016/05/19 Javascript
jquery之别踩白块游戏的简单实现
2016/07/25 Javascript
js窗口震动小程序分享
2016/11/28 Javascript
vue购物车插件编写代码
2017/11/27 Javascript
详解ES6语法之可迭代协议和迭代器协议
2018/01/13 Javascript
使用node.JS中的url模块解析URL信息
2020/02/06 Javascript
各个系统下的Python解释器相关安装方法
2015/10/12 Python
Python实现八大排序算法
2016/08/13 Python
Python pyinotify模块实现对文档的实时监控功能方法
2018/10/13 Python
解决python 无法加载downsample模型的问题
2018/10/25 Python
通过shell+python实现企业微信预警
2019/03/07 Python
tensorflow 实现自定义梯度反向传播代码
2020/02/10 Python
Python内置异常类型全面汇总
2020/05/28 Python
可视化pytorch 模型中不同BN层的running mean曲线实例
2020/06/24 Python
美国最大的城市服装和运动鞋零售商:Jimmy Jazz
2016/11/19 全球购物
苹果音乐订阅:Apple Music
2018/08/02 全球购物
艺术系应届生的自我评价
2013/10/19 职场文书
服装设计专业自荐书范文
2013/12/30 职场文书
自行车租赁公司创业计划书
2014/01/28 职场文书
党员一句话承诺大全
2014/03/28 职场文书
2015年社区国庆节活动总结
2015/07/30 职场文书
background-position百分比原理详解
2021/05/08 HTML / CSS
redis实现共同好友的思路详解
2021/05/26 Redis
HTML页面点击按钮关闭页面的多种方式
2022/12/24 HTML / CSS