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之入门(五)缩进和选择
May 27 Python
Django使用Mysql数据库已经存在的数据表方法
May 27 Python
python初学者,用python实现基本的学生管理系统(python3)代码实例
Apr 10 Python
Python参数类型以及常见的坑详解
Jul 08 Python
Django rest framework jwt的使用方法详解
Aug 08 Python
Python实现基于socket的udp传输与接收功能详解
Nov 15 Python
python3读取csv文件任意行列代码实例
Jan 13 Python
python计算波峰波谷值的方法(极值点)
Feb 18 Python
浅谈Python中re.match()和re.search()的使用及区别
Apr 14 Python
Python如何实现后端自定义认证并实现多条件登陆
Jun 22 Python
Python基于staticmethod装饰器标示静态方法
Oct 17 Python
使用Python判断一个文件是否被占用的方法教程
Dec 16 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
一个简单计数器的源代码
2006/10/09 PHP
php批量缩放图片的代码[ini参数控制]
2011/02/11 PHP
解析php中如何调用用户自定义函数
2013/08/06 PHP
PHPExcel内存泄漏问题解决方法
2015/01/23 PHP
Laravel 框架基于自带的用户系统实现登录注册及错误处理功能分析
2020/04/14 PHP
PHP tp5中使用原生sql查询代码实例
2020/10/28 PHP
利用XMLHTTP传递参数在另一页面执行并刷新本页
2006/10/26 Javascript
js 页面执行时间计算代码
2009/03/04 Javascript
浅谈$(document)和$(window)的区别
2015/07/15 Javascript
基于bootstrap的文件上传控件bootstrap fileinput
2016/12/23 Javascript
thinkphp标签实现bootsrtap轮播carousel实例代码
2017/02/19 Javascript
几行js代码实现自适应
2017/02/24 Javascript
JavaScript Drum Kit 指南(纯 JS 模拟敲鼓效果)
2017/07/23 Javascript
使用jquery DataTable和ajax向页面显示数据列表的方法
2018/08/09 jQuery
bootstrap下拉分页样式 带跳转页码
2018/12/29 Javascript
基于Vue2-Calendar改进的日历组件(含中文使用说明)
2019/04/14 Javascript
详解小程序设置缓存并且不覆盖原有数据
2019/04/15 Javascript
vue实践---根据不同环境,自动转换请求的url地址操作
2020/09/21 Javascript
[01:16:01]VGJ.S vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
python去除空格和换行符的实现方法(推荐)
2017/01/04 Python
Python内置函数—vars的具体使用方法
2017/12/04 Python
pandas使用apply多列生成一列数据的实例
2018/11/28 Python
python 画二维、三维点之间的线段实现方法
2019/07/07 Python
Python中BeautifuSoup库的用法使用详解
2019/11/15 Python
Python matplotlib读取excel数据并用for循环画多个子图subplot操作
2020/07/14 Python
Clarks鞋美国官网:全球领军鞋履品牌
2017/05/13 全球购物
仓库管理专业个人的自我评价
2013/12/30 职场文书
珍惜水资源建议书
2014/03/12 职场文书
四风问题专项整治工作情况报告
2014/10/28 职场文书
优秀护士事迹材料
2014/12/25 职场文书
酒店前台接待岗位职责
2015/04/02 职场文书
无违反计划生育证明格式
2015/06/24 职场文书
小学生运动会广播
2015/08/19 职场文书
Nginx图片服务器配置之后图片访问404的问题解决
2022/03/21 Servers
基于Apache Hudi在Google云构建数据湖平台的思路详解
2022/04/07 Servers