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 getopt 参数处理小示例
Jun 09 Python
Python3学习笔记之列表方法示例详解
Oct 06 Python
Python使用jsonpath-rw模块处理Json对象操作示例
Jul 31 Python
tensorflow使用神经网络实现mnist分类
Sep 08 Python
Python打开文件,将list、numpy数组内容写入txt文件中的方法
Oct 26 Python
Python 保存矩阵为Excel的实现方法
Jan 28 Python
Python 自动登录淘宝并保存登录信息的方法
Sep 04 Python
Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow
Apr 20 Python
keras处理欠拟合和过拟合的实例讲解
May 25 Python
python3的pip路径在哪
Jun 23 Python
python如何实现读取并显示图片(不需要图形界面)
Jul 08 Python
python 基于opencv 绘制图像轮廓
Dec 11 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
如何开发一个虚拟域名系统
2006/10/09 PHP
开启CURL扩展,让服务器支持PHP curl函数(远程采集)
2011/03/19 PHP
php读取mysql乱码,用set names XXX解决的原理分享
2011/12/29 PHP
微信公众平台开发教程⑤ 微信扫码支付模式介绍
2019/04/10 PHP
利用js跨页面保存变量做菜单的方法
2008/01/17 Javascript
ext 代码生成器
2009/08/07 Javascript
Jquery从头学起第四讲 jquery入门教程
2010/08/01 Javascript
js操作table示例(个人心得)
2013/11/29 Javascript
ExtJS4中使用mixins实现多继承示例
2013/12/03 Javascript
解析javascript中鼠标滚轮事件
2015/05/26 Javascript
jQuery实现图片与文字描述左右滑动自动切换的方法
2015/07/27 Javascript
使用getBoundingClientRect方法实现简洁的sticky组件的方法
2016/03/22 Javascript
jquery实现垂直和水平菜单导航栏
2020/08/27 Javascript
angularjs 源码解析之injector
2016/08/22 Javascript
深究AngularJS——ng-checked(回写:带真实案例代码)
2017/06/13 Javascript
AngularJS 实现点击按钮获取验证码功能实例代码
2017/07/13 Javascript
js微信分享实现代码
2020/10/11 Javascript
vue.js数据绑定操作详解
2018/04/23 Javascript
AngularJS $http post 传递参数数据的方法
2018/10/09 Javascript
解决vue中的无限循环问题
2020/07/27 Javascript
JavaScript实现点击图片换背景
2020/11/20 Javascript
Python入门之后再看点什么好?
2018/03/05 Python
Python自动化完成tb喵币任务的操作方法
2019/10/30 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例
2020/03/06 Python
HTML5 source标签:媒介元素定义媒介资源
2018/01/29 HTML / CSS
HTML5实现应用程序缓存(Application Cache)
2020/06/16 HTML / CSS
Stylenanda中文站:韩国一线网络服装品牌
2016/12/22 全球购物
同程旅游英文网站:LY.com
2018/11/13 全球购物
C#实现启动一个进程
2016/10/01 面试题
商务英语毕业生自荐信范文
2013/11/08 职场文书
幼儿园园长岗位职责
2013/11/26 职场文书
消防器材管理制度
2014/01/28 职场文书
市场开发与营销专业求职信范文
2014/05/01 职场文书
2016年主题党日活动总结
2016/04/05 职场文书
基于Python编写简易版的天天跑酷游戏的示例代码
2022/03/23 Python
GoFrame基于性能测试得知grpool使用场景
2022/06/21 Golang