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去除列表中重复元素的方法
Mar 20 Python
python实现文本文件合并
Dec 29 Python
python基础教程之匿名函数lambda
Jan 17 Python
python使用Tesseract库识别验证
Mar 21 Python
Python使用while循环花式打印乘法表
Jan 28 Python
Python如何处理大数据?3个技巧效率提升攻略(推荐)
Apr 15 Python
浅析Python与Mongodb数据库之间的操作方法
Jul 01 Python
利用python计算windows全盘文件md5值的脚本
Jul 27 Python
python中web框架的自定义创建
Sep 08 Python
python代码如何实现余弦相似性计算
Feb 09 Python
python常用运维脚本实例小结
Feb 14 Python
Python HTMLTestRunner如何下载生成报告
Sep 04 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的简单采集数据入库程序【续篇】
2014/07/30 PHP
greybox——不开新窗口看新的网页
2007/02/20 Javascript
javascript removeChild 使用注意事项
2009/04/11 Javascript
jquery Firefox3.5中操作select的问题
2009/07/10 Javascript
jQuery数据显示插件整合实现代码
2011/10/24 Javascript
自动设置iframe大小的jQuery代码
2013/09/11 Javascript
详解JavaScript中undefined与null的区别
2014/03/29 Javascript
JS截取url中问号后面参数的值信息
2014/04/29 Javascript
Javascript中设置默认参数值示例
2014/09/11 Javascript
15个jquery常用方法、小技巧分享
2015/01/13 Javascript
jQuery实现浮动层随浏览器滚动条滚动的方法
2015/09/22 Javascript
使用jQuery在移动页面上添加按钮和给按钮添加图标
2015/12/04 Javascript
省市二级联动小案例讲解
2016/07/24 Javascript
原生JavaScript实现精美的淘宝轮播图效果示例【附demo源码下载】
2017/05/27 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
2018/09/13 Javascript
其实你可以少写点if else与switch(推荐)
2019/01/10 Javascript
详解VUE Element-UI多级菜单动态渲染的组件
2019/04/25 Javascript
运用js实现图层拖拽的功能
2019/05/24 Javascript
[39:21]LGD vs OG 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.24
2019/09/10 DOTA
python Django连接MySQL数据库做增删改查
2013/11/07 Python
python学习数据结构实例代码
2015/05/11 Python
Python中的lstrip()方法使用简介
2015/05/19 Python
Python多线程threading和multiprocessing模块实例解析
2018/01/29 Python
Tensorflow 合并通道及加载子模型的方法
2018/07/26 Python
python读取TXT每行,并存到LIST中的方法
2018/10/26 Python
Python datetime包函数简单介绍
2019/08/28 Python
在pycharm中配置Anaconda以及pip源配置详解
2019/09/09 Python
python实现图像拼接功能
2020/03/23 Python
pycharm远程连接服务器并配置python interpreter的方法
2020/12/23 Python
20行代码教你用python给证件照换底色的方法示例
2021/02/05 Python
签约仪式主持词
2014/03/19 职场文书
学生会宣传部部长竞选演讲稿
2014/04/25 职场文书
小学生民族团结演讲稿
2014/08/27 职场文书
反四风个人对照检查材料思想汇报
2014/09/25 职场文书
学校领导班子成员查摆问题及整改措施
2014/10/28 职场文书
前端JavaScript大管家 package.json
2021/11/02 Javascript