百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Python—编写单例的五种方法(py单例模式)

off999 2024-10-05 19:48 40 浏览 0 评论


为什么选择单例模式

单例模式是一种常见的软件设计模式,其主要目的是确保一个类只有一个实例存在。当您希望一个类的一个实例只出现在整个系统中时,单例对象就派上用场了。

例如,一个服务器程序的配置信息保存在一个文件中,客户端通过一个AppConfig类来读取配置文件信息。

如果程序运行过程中需要在多处使用配置文件的内容,也就是说AppConfig需要在多处创建对象的实例,导致AppConfig系统中存在多个实例对象,会严重浪费内存资源,尤其是配置文件内容很多的情况下。

事实上,对于像这样的类AppConfig,我们希望在程序运行期间只存在一个对象实例。

在 Python 中编写单例的 5 种方法

在 Python 中,我们可以通过几种方式实现单例模式:

使用模块

其实Python的模块是天生的单例模式,因为第一次导入模块时,会生成一个.pyc文件,第二次导入时,.pyc直接加载文件,不会再次执行模块代码.

因此,我们只需要在一个模块中定义相关的函数和数据就可以得到一个单例对象。如果我们真的想要一个单例类,可以考虑这样做:

class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()

把上面的代码保存在文件mysingleton.py中,当你要使用的时候,直接在其他文件中导入这个文件中的对象,这个对象就是单例模式的对象。

from mysingleton import singleton

使用装饰器

装饰器可用于将函数和类包装或封装在@包装器中。如果您想了解更多细节,请查看我的 Python 装饰器文章:“ ”。

Decorator修饰的类或函数,本质上已经不是原来的类或函数了。但实际上,包装后的新对象仍然具有被包装对象的属性。

在Python中,我们往往只需要实现一个装饰器,然后使用装饰器作用于一个只有一个实例的类。这样,你只需要实现这样一个装饰器就可以作用于任何你想要拥有唯一实例的类。

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x


a1 = A(2)
a2 = A(3)

函数声明了一个属于函数作用域的局部Singleton变量。每次调用都会创建一个新的独立函数(所谓的闭包)。_instancesSingletonSingleton_singleton

根据 LEGB 规则,封闭范围的变量是“继承”(这在技术上是不正确的,但简化了解释)到闭包。这意味着,_instances对于特定_singleton()函数的每次调用都将保持相同的对象。

由于每次调用A()actually_singletion被调用,并且_instances每次调用此特定函数都保持不变_singleton,我们可以实现这样的效果,即只创建一次类,并且每次后续调用都将返回相同的对象。

使用类方法

class Singleton(object):

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

总的来说,大家认为这样就完成了单例模式,但是在使用多线程的时候会出现问题:

class Singleton(object):

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

import threading

def task(arg):
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

程序执行后,打印结果如下:

好像没什么问题,那是因为执行速度太快了,如果__init__方法里面有一些IO操作,就会发现问题。

time.sleep下面我们模拟一下,我们在上面的方法中加入如下代码:__init__

class Singleton(object):

    def __init__(self):
        import time
        time.sleep(1)

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

import threading

def task(arg):
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

重新执行程序后,结果如下:

出现问题!上述方式创建的单例不支持多线程。

解决办法:锁定它!解锁部分并发执行,加锁部分串行执行,降低了速度,但保证了数据安全。

import time
import threading


class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        time.sleep(1)

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance()
    print(obj)
    

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()


time.sleep(20)
obj = Singleton.instance()
print(obj)

重新执行程序后,结果如下:

使用“__new__”方法

从上面的例子我们可以知道,我们在实现单例的时候,为了保证线程安全,需要在内部加一把锁。

我们知道,当我们实例化一个对象时,首先会执行__new__类的方法(没写的时候默认调用object.__new__),实例化对象;然后执行__init__类的方法来初始化对象,我们可以做的都是基于this,实现了单例模式。

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = object.__new__(cls)  
        return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

打印结果如下:

这样使用单例模式,以后实例化对象的时候,实例化对象的方法和平时一样obj = Singleton()

使用元类

首先我们需要明白:

  1. 类是由类型创建的。创建类时__init__自动执行该class()类型的方法,执行该类型__call__的方法(类的__new__方法,类的方法__init__
  2. 对象由类创建。对象创建时__init__自动执行类的方法,object()执行__call__类的方法

例如:

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        pass
# Execute Type's __call__ method, call Foo.__new__() to create object, then call Foo.__init__() to initialize
obj = Foo()
# Execute Foo.__call__ method
obj()

实现单例:

import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)

运行输出:


如果你发现我的任何文章对你有帮助或者有用,麻烦点赞或者转发。 谢谢!

相关推荐

安全教育登录入口平台(安全教育登录入口平台官网)

122交通安全教育怎么登录:122交通网的注册方法是首先登录网址http://www.122.cn/,接着打开网页后,点击右上角的“个人登录”;其次进入邮箱注册,然后进入到注册页面,输入相关信息即可完...

大鱼吃小鱼经典版(大鱼吃小鱼经典版(经典版)官方版)

大鱼吃小鱼小鱼吃虾是于谦跟郭麒麟的《我的棒儿呢?》郭德纲说于思洋郭麒麟作诗的相声,最后郭麒麟做了一首,师傅躺在师母身上大鱼吃小鱼小鱼吃虾虾吃水水落石出师傅压师娘师娘压床床压地地动山摇。...

谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)

要想将ppt免费转换为pdf的话,我们建议大家可以下一个那个wps,如果你是会员的话,可以注册为会员,这样的话,在wps里面的话,就可以免费将ppt呢转换为pdfpdf之后呢,我们就可以直接使用,不需要去直接不需要去另外保存,为什么格式转...

2026-02-04 09:03 off999

电信宽带测速官网入口(电信宽带测速官网入口app)

这个网站看看http://www.swok.cn/pcindex.jsp1.登录中国电信网上营业厅,宽带光纤,贴心服务,宽带测速2.下载第三方软件,如360等。进行在线测速进行宽带测速时,尽...

植物大战僵尸95版手机下载(植物大战僵尸95 版下载)

1可以在应用商店或者游戏平台上下载植物大战僵尸95版手机游戏。2下载教程:打开应用商店或者游戏平台,搜索“植物大战僵尸95版”,找到游戏后点击下载按钮,等待下载完成即可安装并开始游戏。3注意:确...

免费下载ppt成品的网站(ppt成品免费下载的网站有哪些)

1、Chuangkit(chuangkit.com)直达地址:chuangkit.com2、Woodo幻灯片(woodo.cn)直达链接:woodo.cn3、OfficePlus(officeplu...

2025世界杯赛程表(2025世界杯在哪个国家)

2022年卡塔尔世界杯赛程公布,全部比赛在卡塔尔境内8座球场举行,2022年,决赛阶段球队全部确定。揭幕战于当地时间11月20日19时进行,由东道主卡塔尔对阵厄瓜多尔,决赛于当地时间12月18日...

下载搜狐视频电视剧(搜狐电视剧下载安装)

搜狐视频APP下载好的视频想要导出到手机相册里方法如下1、打开手机搜狐视频软件,进入搜狐视频后我们点击右上角的“查找”,找到自已喜欢的视频。2、在“浏览器页面搜索”窗口中,输入要下载的视频的名称,然后...

pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
永久免费听歌网站(丫丫音乐网)

可以到《我爱音乐网》《好听音乐网》《一听音乐网》《YYMP3音乐网》还可以到《九天音乐网》永久免费听歌软件有酷狗音乐和天猫精灵,以前要跳舞经常要下载舞曲,我从QQ上找不到舞曲下载就从酷狗音乐上找,大多...

音乐格式转换mp3软件(音乐格式转换器免费版)

有两种方法:方法一在手机上操作:1、进入手机中的文件管理。2、在其中选择“音乐”,将显示出手机中的全部音乐。3、点击“全选”,选中所有音乐文件。4、点击屏幕右下方的省略号图标,在弹出菜单中选择“...

电子书txt下载(免费的最全的小说阅读器)

1.Z-library里面收录了近千万本电子书籍,需求量大。2.苦瓜书盘没有广告,不需要账号注册,使用起来非常简单,直接搜索预览下载即可。3.鸠摩搜书整体风格简洁清晰,书籍资源丰富。4.亚马逊图书书籍...

最好免费观看高清电影(播放免费的最好看的电影)

在目前的网上选择中,IMDb(互联网电影数据库)被认为是最全的电影网站之一。这个网站提供了各种类型的电影和电视节目的海量信息,包括剧情介绍、演员表、评价、评论等。其还提供了有关电影制作背后的详细信息,...

孤单枪手2简体中文版(孤单枪手2简体中文版官方下载)

要将《孤胆枪手2》游戏的征兵秘籍切换为中文,您可以按照以下步骤进行操作:首先,打开游戏设置选项,通常可以在游戏主菜单或游戏内部找到。然后,寻找语言选项或界面选项,点击进入。在语言选项中,选择中文作为游...

取消回复欢迎 发表评论: