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中为feedparser设置超时时间避免堵塞
Sep 28 Python
Python的Flask框架应用程序实现使用QQ账号登录的方法
Jun 07 Python
python中os和sys模块的区别与常用方法总结
Nov 14 Python
python实现图片彩色转化为素描
Jan 15 Python
自学python的建议和周期预算
Jan 30 Python
Python+selenium点击网页上指定坐标的实例
Jul 05 Python
如何安装2019Pycharm最新版本(详细教程)
Sep 26 Python
Python 异步协程函数原理及实例详解
Nov 13 Python
python读取yaml文件后修改写入本地实例
Apr 27 Python
基于Python实现下载网易音乐代码实例
Aug 10 Python
python3中celery异步框架简单使用+守护进程方式启动
Jan 20 Python
python实现scrapy爬虫每天定时抓取数据的示例代码
Jan 27 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中的stdClass类
2014/04/18 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(九)
2014/06/24 PHP
PHP模板引擎Smarty内置变量调解器用法详解
2016/04/11 PHP
yii2中关于加密解密的那些事儿
2018/06/12 PHP
php 中phar包的使用教程详解
2018/10/26 PHP
ExtJS GTGrid 简单用户管理
2009/07/01 Javascript
网页禁用右键实现代码(JavaScript代码)
2009/10/29 Javascript
在网页中使用document.write时遭遇的奇怪问题
2010/08/24 Javascript
JQuery datepicker 使用方法
2011/05/20 Javascript
jQuery下的动画处理总结
2013/10/10 Javascript
js实现在网页上简单显示时间的方法
2015/03/02 Javascript
jQuery团购倒计时特效实现方法
2015/05/07 Javascript
javascript实现根据时间段显示问候语的方法
2015/06/18 Javascript
谈谈AngularJs中的隐藏和显示
2015/12/09 Javascript
jquery ztree实现模糊搜索功能
2016/02/25 Javascript
JS中使用变量保存arguments对象的方法
2016/06/03 Javascript
Angular.js中$apply()和$digest()的深入理解
2016/10/13 Javascript
微信小程序 利用css实现遮罩效果实例详解
2017/01/21 Javascript
Bootstrap栅格系统使用方法及页面调整变形的解决方法
2017/03/10 Javascript
微信小程序定位当前城市的方法
2018/07/19 Javascript
在element-ui的select下拉框加上滚动加载
2019/04/18 Javascript
Vue使用vue-recoure + http-proxy-middleware + vuex配合promise实现基本的跨域请求封装
2019/10/21 Javascript
Vue axios 跨域请求无法带上cookie的解决
2020/09/08 Javascript
jquery实现加载更多"转圈圈"效果(示例代码)
2020/11/09 jQuery
wxPython中文教程入门实例
2014/06/09 Python
浅谈Python中的数据类型
2015/05/05 Python
Python实现自定义函数的5种常见形式分析
2018/06/16 Python
用Python写一个模拟qq聊天小程序的代码实例
2019/03/06 Python
Python转换字典成为对象,可以用"."方式访问对象属性实例
2020/05/11 Python
Pandas之缺失数据的实现
2021/01/06 Python
python 装饰器重要在哪
2021/02/14 Python
Parfumdreams英国:香水和化妆品
2019/05/10 全球购物
英国时尚泳装品牌:Maru Swimwear
2019/10/06 全球购物
企业领导对照检查材料
2014/08/20 职场文书
公司仓管员岗位职责
2015/04/01 职场文书
茶花女读书笔记
2015/06/29 职场文书