首页 后端开发 Python教程 Python创建单例模式的5种常用方法

Python创建单例模式的5种常用方法

Oct 18, 2016 am 09:31 AM

所谓单例,是指一个类的实例从始至终只能被创建一次。

方法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就是它自己类的唯一对象了。


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何解决Linux终端中查看Python版本时遇到的权限问题? 如何解决Linux终端中查看Python版本时遇到的权限问题? Apr 01, 2025 pm 05:09 PM

Linux终端中查看Python版本时遇到权限问题的解决方法当你在Linux终端中尝试查看Python的版本时,输入python...

在Python中如何高效地将一个DataFrame的整列复制到另一个结构不同的DataFrame中? 在Python中如何高效地将一个DataFrame的整列复制到另一个结构不同的DataFrame中? Apr 01, 2025 pm 11:15 PM

在使用Python的pandas库时,如何在两个结构不同的DataFrame之间进行整列复制是一个常见的问题。假设我们有两个Dat...

如何在10小时内通过项目和问题驱动的方式教计算机小白编程基础? 如何在10小时内通过项目和问题驱动的方式教计算机小白编程基础? Apr 02, 2025 am 07:18 AM

如何在10小时内教计算机小白编程基础?如果你只有10个小时来教计算机小白一些编程知识,你会选择教些什么�...

如何在使用 Fiddler Everywhere 进行中间人读取时避免被浏览器检测到? 如何在使用 Fiddler Everywhere 进行中间人读取时避免被浏览器检测到? Apr 02, 2025 am 07:15 AM

使用FiddlerEverywhere进行中间人读取时如何避免被检测到当你使用FiddlerEverywhere...

什么是正则表达式? 什么是正则表达式? Mar 20, 2025 pm 06:25 PM

正则表达式是在编程中进行模式匹配和文本操作的强大工具,从而提高了各种应用程序的文本处理效率。

Uvicorn是如何在没有serve_forever()的情况下持续监听HTTP请求的? Uvicorn是如何在没有serve_forever()的情况下持续监听HTTP请求的? Apr 01, 2025 pm 10:51 PM

Uvicorn是如何持续监听HTTP请求的?Uvicorn是一个基于ASGI的轻量级Web服务器,其核心功能之一便是监听HTTP请求并进�...

哪些流行的Python库及其用途? 哪些流行的Python库及其用途? Mar 21, 2025 pm 06:46 PM

本文讨论了诸如Numpy,Pandas,Matplotlib,Scikit-Learn,Tensorflow,Tensorflow,Django,Blask和请求等流行的Python库,并详细介绍了它们在科学计算,数据分析,可视化,机器学习,网络开发和H中的用途

Python中如何通过字符串动态创建对象并调用其方法? Python中如何通过字符串动态创建对象并调用其方法? Apr 01, 2025 pm 11:18 PM

在Python中,如何通过字符串动态创建对象并调用其方法?这是一个常见的编程需求,尤其在需要根据配置或运行...

See all articles