5种Python单例模式的实现方式
作者:lijiao 发布时间:2023-05-17 12:57:14
本文为大家分享了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就是它自己类的唯一对象了。
猜你喜欢
- 介绍psutil能够轻松实现获取系统运行的进程和系统利用率。导入模块import psutils获取系统性能信息CPU信息使用cpu_tim
- 动机: 查询功能是我们在网站上见过的最普遍也是最常用的一个功能模块了。以往的信息查询都是连接到数据库的,每一次点击都必须要后台数据库的支持。
- bookheader.asp Recommended Books for <%=session(&quo
- 代码如下:<% Response.Buffer = True '一般情况下,当用户请求
- 为什么使用生产者消费者模式在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者
- 测试方法首先使用implode, serialize, json_encode, msgpack_pack创建四个文本文件,用于测试。创建代
- 运行环境: python 3.6.0今天处于练习的目的,就用 python 写了一个百度翻译,是如何做到的呢,其实呢就是拿到接口,通过这个接
- 1、shutdown normal 正常方式关闭数据库。 2、shutdown imme
- 方法一:<script language="JavaScript"> <!--
- 使用非对称加密主要是借助openssl的公钥和私钥,用公钥加密私钥解密,或者私钥加密公钥解密。1.安装openssl和php的openssl
- 1.如何将Query String传送到另一个ASP文件去?Response.Redirect("second.asp? 
- 本文主要包括三大方面,大家仔细学习。1、导航栏中的表单导航栏中的表单不是使用 Bootstrap 表单 章节中所讲到的默认的 class,它
- 文通过一个操作实例来说明SQL中主标识列IDENTITY的使用技巧。要求:在 sql server 2005中,建立数据表book,在表bo
- 软件版本Python 2.7.13; Win 10场景描述1、使用python读取指定长度的文本;2、使用python读取某一范围内的文本。
- 1、说明使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是
- 在网站开发的时候经常要用chr(),但本人比较懒没时间记那么多。于是到用到的时候就查,这样麻烦。现在将它写出来方便以后用到查,也方便大家!c
- innerHTML,outerHTML innerHTML检索或设置标签内的内容;outerHTML检索或设置整个标签的内容(包含标签)。&
- 发现ie7的空格间距要比ie6/firefox/opera的都要宽一点。比如有时候排版的时候,我会采用简单的空格来分隔。<div&nb
- 索引是以表列为基础的数据库对象。索引中保存着表中排序的索引列,并且纪录了索引列在数据库表中的物理存储位置,实现了表中数据的逻辑排序。通过索引
- 本文实例分析了LINUX下Oracle数据库用户创建方法。分享给大家供大家参考,具体如下:1)登录linux,以oracle用户登录(如果是