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

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

off999 2024-10-05 19:48 20 浏览 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)

运行输出:


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

相关推荐

python爬取电子课本,送给居家上课的孩子们

在这个全民抗疫的日子,中小学生们也开启了居家上网课的生活。很多没借到书的孩子,不得不在网上看电子课本,有的电子课本是老师发的网络链接,每次打开网页去看,既费流量,也不方便。今天我们就利用python的...

高效办公!Python 批量生成PDF文档是如何做到的?

前言:日常办公中,经常会使用PDF文档,难免需要对PDF文档进行编辑,有时候PDF文档中的大部分内容都是一样的,只是发送对象不同。这种模板套用的场景下,使用Python进行自动化就尤为方便,用最短的时...

如何用Python将PDF完整的转成Word?

PDF文件完整的转为Word,转换后格式排版不会乱,图片等信息完整显示不丢失。这个很简单,有很多方法都可以实现。方法一:Python利用Python将PDF文件转换为Word,有许多库可以帮你实现这一...

使用Python拆分、合并PDF(python合并多个pdf)

知识点使用Python操作PDF!主要内容有:1、PDF拆分;2、PDF合并。在工作中,难免会和PDF打交道,所以掌握一点处理PDF的技能非常有必要,本文将介绍几个常用的功能。PDF拆分很多时候,获取...

10分钟实现PDF转Word神器!看DeepSeek如何用Python解放打工人

开篇痛点每个被PDF折磨过的职场人都懂——领导发来的扫描件要修改,手动抄到Word需要2小时;网上下载的报告想复制数据,却变成乱码…今天我们用Python+DeepSeek,10分钟打造一个智能转换工...

《Python知识手册》,高清全彩pdf版开放下载

Python编程还不懂?今天我要把我参与编写的这套《Python知识手册》免费分享出来,看完文末有惊喜哦。...

利用python进行数据分析,PDF文档给你答案

本书详细介绍利用Python进行操作、处理、清洗和规整数据等方面的具体细节和基本要点。虽然本书的标题是“数据分析”,重点却是Python编程、库,以及用于数据分析的工具。兄弟,毫无套路!PDF版无偿获...

OCRmypdf:一款可以让扫描PDF文件变得可搜索、可复制!

简介在日常工作中,我们经常会接触到各种PDF文件,其中不少是扫描版文档。处理这些扫描PDF时,尽管内容看似完整,但往往无法直接复制或搜索其中的文本。尤其是在需要对大量文档进行文本分析、存档或后期编辑时...

高效的OCR处理工具!让扫描PDF文件变得可搜索、可复制!

在工作中,我们常常遇到各种各样的PDF文件,其中不乏一些扫描版的文档。而在处理扫描的PDF文件时,虽然文件内容看似完整,但你却无法复制、搜索其中的文本。特别是对大量文档需要进行文本分析、存档、或者...

三步教你用Elasticsearch+PyMuPDF实现PDF大文件秒搜!

面对100页以上的大型PDF文件时,阅读和搜索往往效率低下。传统关系型数据库在处理此类数据时容易遇到性能瓶颈,而Elasticsearch凭借其强大的全文检索和分布式架构,成为理想解决方案。通过...

用 Python 去除 PDF 水印,你学会吗?

今天介绍下用Python去除PDF(图片)的水印。思路很简单,代码也很简洁。首先来考虑Python如何去除图片的水印,然后再将思路复用到PDF上面。这张图片是前几天整理《数据结构和算法...

扫描PDF档案效率提升300%!OCRmyPDF:告别无法搜索的PDF噩梦,这款26K Star的开源神器让文本识别轻松上手!

要在PDF中搜索某个关键词,结果发现啥也找不到?这种情况大多数人都遇到过吧,特别是处理扫描文档或图片PDF时。就在前几天,我还在为这事抓狂呢!后来无意中发现了OCRmyPDF这个宝藏项目...简直就...

Python自动化办公之PDF版本发票识别并提取关键信息教程(上篇)

大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Python自动化办公发票数据处理的问题,一起来看看吧。二、实现过程这个问题在实际工作中还是非常常见的,实用性和通用性都比...

PDF解锁神器:用PyMuPDF与pdfplumber告别手动提取

前言大家好,今天咱们来聊聊如何用Python中的PyMuPDF和pdfplumber库,轻松提取PDF文件里的文本和元数据。你是否曾经在处理一个复杂的PDF文件时,感到信息难以触及,提取过程让人抓狂?...

《Python知识手册》,高清pdf免费获取

今天我要把我参与编写的这套《Python知识手册》免费分享出来,真正弘扬Python开源精神!手册的部分页面如下:获取方式:...

取消回复欢迎 发表评论: