5种Python单例模式的实现方式


Posted in Python onJanuary 14, 2016

本文为大家分享了Python创建单例模式的5种常用方法,供大家参考,具体内容如下

所谓单例,是指一个类的实例从始至终只能被创建一次。
方法1:
如果想使得某个类从始至终最多只有一个实例,使用__new__方法会很简单。Python中类是通过__new__来创建实例的:

class Singleton(object):
  def __new__(cls,*args,**kwargs):
    if not hasattr(cls,'_inst'):
      cls._inst=super(Singleton,cls).__new__(cls,*args,**kwargs)
    return cls._inst
if __name__=='__main__':
  class A(Singleton):
    def __init__(self,s):
      self.s=s   
  a=A('apple')  
  b=A('banana')
  print id(a),a.s
  print id(b),b.s

结果:
29922256 banana
29922256 banana
通过__new__方法,将类的实例在创建的时候绑定到类属性_inst上。如果cls._inst为None,说明类还未实例化,实例化并将实例绑定到cls._inst,以后每次实例化的时候都返回第一次实例化创建的实例。注意从Singleton派生子类的时候,不要重载__new__。
方法2:
有时候我们并不关心生成的实例是否具有同一id,而只关心其状态和行为方式。我们可以允许许多个实例被创建,但所有的实例都共享状态和行为方式:

class Borg(object):
  _shared_state={}
  def __new__(cls,*args,**kwargs):
    obj=super(Borg,cls).__new__(cls,*args,**kwargs)
    obj.__dict__=cls._shared_state
    return obj

将所有实例的__dict__指向同一个字典,这样实例就共享相同的方法和属性。对任何实例的名字属性的设置,无论是在__init__中修改还是直接修改,所有的实例都会受到影响。不过实例的id是不同的。要保证类实例能共享属性,但不和子类共享,注意使用cls._shared_state,而不是Borg._shared_state。
因为实例是不同的id,所以每个实例都可以做字典的key:

if __name__=='__main__':
  class Example(Borg):
    pass
  a=Example()
  b=Example()
  c=Example()
  adict={}
  j=0
  for i in a,b,c:
    adict[i]=j
    j+=1
  for i in a,b,c:
    print adict[i]

结果:
0
1
2
如果这种行为不是你想要的,可以为Borg类添加__eq__和__hash__方法,使其更接近于单例模式的行为:

class Borg(object):
  _shared_state={}
  def __new__(cls,*args,**kwargs):
    obj=super(Borg,cls).__new__(cls,*args,**kwargs)
    obj.__dict__=cls._shared_state
    return obj
  def __hash__(self):
    return 1
  def __eq__(self,other):
    try:
      return self.__dict__ is other.__dict__
    except:
      return False
if __name__=='__main__':
  class Example(Borg):
    pass
  a=Example()
  b=Example()
  c=Example()
  adict={}
  j=0
  for i in a,b,c:
    adict[i]=j
    j+=1
  for i in a,b,c:
    print adict[i]

结果:
2
2
2
所有的实例都能当一个key使用了。
方法3
当你编写一个类的时候,某种机制会使用类名字,基类元组,类字典来创建一个类对象。新型类中这种机制默认为type,而且这种机制是可编程的,称为元类__metaclass__ 。

class Singleton(type):
  def __init__(self,name,bases,class_dict):
    super(Singleton,self).__init__(name,bases,class_dict)
    self._instance=None
  def __call__(self,*args,**kwargs):
    if self._instance is None:
      self._instance=super(Singleton,self).__call__(*args,**kwargs)
    return self._instance
if __name__=='__main__':
  class A(object):
    __metaclass__=Singleton    
  a=A()
  b=A()
  print id(a),id(b)

结果:
34248016 34248016
id是相同的。
例子中我们构造了一个Singleton元类,并使用__call__方法使其能够模拟函数的行为。构造类A时,将其元类设为Singleton,那么创建类对象A时,行为发生如下:
A=Singleton(name,bases,class_dict),A其实为Singleton类的一个实例。
创建A的实例时,A()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__(),这样就将A的所有实例都指向了A的属性_instance上,这种方法与方法1其实是相同的。
 方法4
python中的模块module在程序中只被加载一次,本身就是单例的。可以直接写一个模块,将你需要的方法和属性,写在模块中当做函数和模块作用域的全局变量即可,根本不需要写类。
而且还有一些综合模块和类的优点的方法:

class _singleton(object):
  class ConstError(TypeError):
    pass
  def __setattr__(self,name,value):
    if name in self.__dict__:
      raise self.ConstError
    self.__dict__[name]=value
  def __delattr__(self,name):
    if name in self.__dict__:
      raise self.ConstError
    raise NameError
import sys
sys.modules[__name__]=_singleton()

python并不会对sys.modules进行检查以确保他们是模块对象,我们利用这一点将模块绑定向一个类对象,而且以后都会绑定向同一个对象了。
将代码存放在single.py中:

>>> import single
>>> single.a=1
>>> single.a=2

ConstError
>>> del single.a
ConstError
方法5:
最简单的方法:

class singleton(object):
  pass
singleton=singleton()

将名字singleton绑定到实例上,singleton就是它自己类的唯一对象了。

以上就是Python单例模式的实现方式详细介绍,希望对大家的学习有所帮助。

Python 相关文章推荐
python subprocess 杀掉全部派生的子进程方法
Jan 16 Python
python 求某条线上特定x值或y值的点坐标方法
Jul 09 Python
Python any()函数的使用方法
Oct 28 Python
Python 实现顺序高斯消元法示例
Dec 09 Python
Keras—embedding嵌入层的用法详解
Jun 10 Python
tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this T
Jun 22 Python
在django中实现choices字段获取对应字段值
Jul 12 Python
python实现简单的tcp 文件下载
Sep 16 Python
Python 实现3种回归模型(Linear Regression,Lasso,Ridge)的示例
Oct 15 Python
python给list排序的简单方法
Dec 10 Python
python基础之文件处理知识总结
May 23 Python
Python+Matplotlib图像上指定坐标的位置添加文本标签与注释
Apr 11 Python
Python2.x与Python3.x的区别
Jan 14 #Python
python Django模板的使用方法
Jan 14 #Python
Python数据类型学习笔记
Jan 13 #Python
python基础入门学习笔记(Python环境搭建)
Jan 13 #Python
详解python时间模块中的datetime模块
Jan 13 #Python
Python时间模块datetime、time、calendar的使用方法
Jan 13 #Python
基于Python实现文件大小输出
Jan 11 #Python
You might like
用PHP写的基于Memcache的Queue实现代码
2011/11/27 PHP
使用HMAC-SHA1签名方法详解
2013/06/26 PHP
PHP截断标题且兼容utf8和gb2312编码
2013/09/22 PHP
php实现批量修改文件名称的方法
2016/07/23 PHP
PHP实现的注册,登录及查询用户资料功能API接口示例
2017/06/06 PHP
visual studio code 调试php方法(图文详解)
2017/09/15 PHP
Laravel 5.5官方推荐的Nginx配置学习教程
2017/10/06 PHP
PHP substr()函数参数解释及用法讲解
2017/11/23 PHP
JS宝典学习笔记(下)
2007/01/10 Javascript
JQuery优缺点分析说明
2010/06/09 Javascript
js优化针对IE6.0起作用(详细整理)
2012/12/25 Javascript
jquery操作下拉列表、文本框、复选框、单选框集合(收藏)
2014/01/08 Javascript
JavaScript中的函数的两种定义方式和函数变量赋值
2014/05/12 Javascript
js动态创建标签示例代码
2014/06/09 Javascript
javascript实现汉字转拼音代码分享
2015/04/20 Javascript
JavaScript操作URL的相关内容集锦
2015/10/29 Javascript
实例代码讲解jquery easyui动态tab页
2015/11/17 Javascript
jquery mobile开发常见问题分析
2016/01/21 Javascript
JavaScript数组方法总结分析
2016/05/06 Javascript
用JS动态改变表单form里的action值属性的两种方法
2016/05/25 Javascript
js完整倒计时代码分享
2016/09/18 Javascript
BootStrap下拉菜单和滚动监听插件实现代码
2016/09/26 Javascript
Bootstrap CSS布局之表格
2016/12/17 Javascript
JS解决IOS中拍照图片预览旋转90度BUG的问题
2017/09/13 Javascript
vue基于element的区间选择组件
2018/09/07 Javascript
js prototype深入理解及应用实例分析
2019/11/25 Javascript
jQuery实现容器间的元素拖拽功能
2020/12/01 jQuery
Python内建函数之raw_input()与input()代码解析
2017/10/26 Python
python中使用print输出中文的方法
2018/07/16 Python
在python里从协程返回一个值的示例
2019/02/19 Python
详解torch.Tensor的4种乘法
2020/09/03 Python
CSS3 实现童年的纸飞机
2019/05/05 HTML / CSS
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
优秀中学生事迹材料
2014/01/31 职场文书
小学生环保演讲稿
2014/04/25 职场文书
2015年挂职锻炼工作总结
2014/12/12 职场文书