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 默认参数问题的陷阱
Feb 29 Python
解决python中遇到字典里key值为None的情况,取不出来的问题
Oct 17 Python
Python Django基础二之URL路由系统
Jul 18 Python
Python之数据序列化(json、pickle、shelve)详解
Aug 30 Python
详解Anconda环境下载python包的教程(图形界面+命令行+pycharm安装)
Nov 11 Python
Python对Tornado请求与响应的数据处理
Feb 12 Python
python读取csv文件指定行的2种方法详解
Feb 13 Python
Python基于os.environ从windows获取环境变量
Jun 09 Python
python tkiner实现 一个小小的图片翻页功能的示例代码
Jun 24 Python
Pycharm2020最新激活码|永久激活(附最新激活码和插件的详细教程)
Sep 29 Python
Python代码覆盖率统计工具coverage.py用法详解
Nov 25 Python
Python OpenCV实现图像模板匹配详解
Apr 07 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自毁程序(慎用)
2015/07/09 PHP
2款PHP无限级分类实例代码
2015/11/11 PHP
详解php协程知识点
2018/09/21 PHP
php实现简单的守护进程创建、开启与关闭操作
2019/08/13 PHP
php新建文件的方法实例
2019/09/26 PHP
jQuery实现分章节锚点“回到顶部”动画特效代码
2015/10/23 Javascript
JS中call/apply、arguments、undefined/null方法详解
2016/02/15 Javascript
JavaScript6 let 新语法优势介绍
2016/07/15 Javascript
js获取腾讯视频ID的方法
2016/10/03 Javascript
创建一般js对象的几种方式
2017/01/19 Javascript
JavaScript实现各种排序的代码详解
2017/08/28 Javascript
ES6扩展运算符用法实例分析
2017/10/31 Javascript
微信小程序实现左侧滑动导航栏
2020/04/08 Javascript
vue基于better-scroll仿京东分类列表
2020/06/30 Javascript
微信小程序实现天气预报功能(附源码)
2020/12/10 Javascript
[05:49]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS 选手采访
2021/03/11 DOTA
Python采用raw_input读取输入值的方法
2014/08/18 Python
用Python进行TCP网络编程的教程
2015/04/29 Python
为Python的web框架编写MVC配置来使其运行的教程
2015/04/30 Python
详解C++编程中一元运算符的重载
2016/01/19 Python
使用Python的turtle模块画图的方法
2017/11/15 Python
python中dir()与__dict__属性的区别浅析
2018/12/10 Python
python3 实现调用串口功能
2019/12/26 Python
美国在线鞋类零售商:LifeStride
2019/06/09 全球购物
TOWER London官网:鞋子、靴子、运动鞋等
2019/07/14 全球购物
ECCO英国官网:丹麦鞋履品牌
2019/09/03 全球购物
银行个人求职自荐信范文
2013/12/16 职场文书
大学四年个人的自我评价
2014/02/26 职场文书
答谢会策划方案
2014/05/12 职场文书
付款委托书范本
2014/10/05 职场文书
开场白怎么写
2015/06/01 职场文书
中秋节随笔
2015/08/15 职场文书
会计专业自荐信范文
2019/05/22 职场文书
简历上的自我评价,该怎么写呢?
2019/06/13 职场文书
中国十大神话动漫电影排行榜 哪吒登顶 白蛇缘起排第七
2022/03/21 国漫
第四次工业革命,打工人与机器人的竞争
2022/04/21 数码科技