python中的设计模式:单例模式

设计模式

设计模式的确切数量并没有一个统一的标准,因为不同的资料和文献可能会对设计模式的定义和分类有所不同。然而,最常见的设计模式集合是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides这四位作者在他们的著作《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)中提出的23种模式。

这23种模式通常被分为三类:

  1. 创建型模式(Creational Patterns):提供了对象创建的机制,能够增加已有代码的灵活性和可重用性。

    • 单例模式(Singleton)
    • 工厂方法模式(Factory Method)
    • 抽象工厂模式(Abstract Factory)
    • 建造者模式(Builder)
    • 原型模式(Prototype)
  2. 结构型模式(Structural Patterns):关注类和对象的组合,以形成更大的结构。

    • 适配器模式(Adapter)
    • 装饰器模式(Decorator)
    • 代理模式(Proxy)
    • 外观模式(Facade)
    • 桥接模式(Bridge)
    • 组合模式(Composite)
    • 享元模式(Flyweight)
  3. 行为型模式(Behavioral Patterns):涉及对象之间的通信,以及在不同对象之间分配责任和算法。

    • 责任链模式(Chain of Responsibility)
    • 命令模式(Command)
    • 解释器模式(Interpreter)
    • 迭代器模式(Iterator)
    • 中介者模式(Mediator)
    • 备忘录模式(Memento)
    • 观察者模式(Observer)
    • 状态模式(State)
    • 策略模式(Strategy)
    • 模板方法模式(Template Method)
    • 访问者模式(Visitor)

除了这23种模式之外,随着软件开发实践的发展,其他的设计模式也被提出和使用。例如,有一些设计模式专门针对特定领域,如用户界面设计模式、企业应用架构模式等。因此,设计模式的总数可能会随着时间和社区的发展而增加。不过,上述23种模式是最基础和最广泛认可的设计模式集合。

单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局状态或者需要频繁创建和销毁对象时非常有用,因为它可以减少资源消耗并提高性能。

应用场景

  • 当你想要控制一个实例的创建,确保在整个应用程序中只使用一个实例时。
  • 当一个实例需要频繁地创建和销毁,而这些操作开销很大时。
  • 当一个实例需要作为其他实例的工厂时。
  • 当需要一个全局访问点,但希望避免在每次调用时创建新实例时。

特点

  • 唯一性:单例模式确保一个类只有一个实例存在。
  • 全局访问:提供了一个全局访问点,可以方便地获取到这个唯一的实例。
  • 延迟初始化:单例实例可以在真正需要时才进行初始化,这有助于提高程序启动速度和节省资源。

优缺点

  • 优点

    • 资源节省:由于只有一个实例,可以减少资源消耗。
    • 全局访问点:提供一个统一的访问点,方便获取实例。
    • 控制实例个数:确保某个类只有一个实例,便于控制。
  • 缺点

    • 可扩展性差:单例模式把控制对象实例的生成全权交给了类本身,无法进行扩展。
    • 滥用风险:单例模式容易滥用,导致系统难以维护和调试。
    • 线程安全问题:在多线程环境下,需要考虑线程安全问题。

python中的实现方式

1. 使用模块

Python的模块在一个Python解释器进程中只加载一次,因此模块自然就是一个单例。你可以简单地将你的类定义在一个模块中,然后通过导入该模块来访问这个类的唯一实例。

class Singleton:pass_singleton = Singleton()def get_singleton():return _singleton

使用时,只需从模块中获取实例:

# 使用模块导入实现
get_singleton = __import__("64 单例模式(导入的模块)").get_singleton
print(get_singleton())
print(get_singleton())
print(get_singleton())

2. 使用类变量和覆写 __new__ 方法

你可以覆写类的 __new__ 方法来控制实例的创建过程,确保只创建一个实例。

# 使用类变量和覆写 `__new__` 方法
class Singleton1:_instance = Nonedef __new__(cls, *args, **kwargs):if not cls._instance:cls._instance = super().__new__(cls, *args, **kwargs)return cls._instancea = Singleton1()
b = Singleton1()
print(a)
print(b)
print(a == b)

3. 使用装饰器

创建一个装饰器来封装单例的逻辑,使得你可以简单地通过装饰一个类来使其成为单例。

第一次尝试实现,发现报错

# TypeError: 'Singleton2' object is not callable
# 装饰器必须放回的是个函数!不然Singleton2()返回是个实例,当然不可以再调用:Singleton2()()
def singleton(cls, *args, **kwargs):_instance = {}if cls not in _instance:_instance[cls] = cls(*args, **kwargs)return _instance[cls]@singleton
class Singleton2:passa = Singleton2()
b = Singleton2()
print(a)
print(b)print(a == b)

执行发现会报错,原因如下:

  1. _instance字典被定义在了singleton函数内部,这意味着每次调用装饰器时都会重新创建一个新的空字典。因此,即使同一个类多次实例化也无法保证单例模式。
  2. 装饰器应该返回一个新的函数或者类,而不是直接返回实例对象。在当前写法中,当使用@singleton修饰Singleton2类时,并没有正确地返回一个可调用对象(比如工厂函数),而是直接返回了Singleton2类的实例。

修正后:

def singleton(cls):instances = {}def get_instance(*args, **kwargs):if cls not in instances:instances[cls] = cls(*args, **kwargs)return instances[cls]return get_instance@singleton
class Singleton2:passa = Singleton2()
b = Singleton2()
print(a)
print(b)print(a == b)

修正版本中:

  • 使用了一个名为 instances 的字典来存储类及其对应的单一实例。
  • 定义了一个内部包装函数 get_instance() 来检查给定类是否已经有对应的单一实例存在于字典中;如果不存在,则创建一个新实例并存储起来;如果已存在,则直接返回该实例。
  • 最终返回这个内部包装函数而非直接返回类或者类的新实例。这样确保了每次通过该装饰器获取到的都是同一个单一示例。
*args, **kwargs,这两个参数有什么用?

*args**kwargs 是用来接收任意数量的位置参数和关键字参数的。这两个参数在装饰器模式中非常有用,因为它们允许你创建一个通用装饰器,该装饰器可以适应任何具有不同初始化参数的类。

即使在示例代码中没有显式地传递任何值给 Singleton2 类,保留 *args**kwargs 也是一个好习惯。这样做可以增加代码的灵活性和可重用性。如果将来你需要实现一个需要初始化参数的单例类,那么已经存在的单例装饰器就可以直接使用了。

例如:

def singleton(cls):instances = {}print("装饰器调用一次")def get_instance(*args, **kwargs):if cls not in instances:instances[cls] = cls(*args, **kwargs)return instances[cls]# def get_instance():#     if cls not in instances:#         instances[cls] = cls()#     return instances[cls]return get_instance@singleton
class Singleton2:def __init__(self, data):self.data = dataa = Singleton2("a")
b = Singleton2("b")
print(a)
print(a.data)
print(b)
print(b.data)print(a == b)

如果我们没有在定义 get_instance() 函数时包含 *args**kwargs 参数,那么上面这段代码就会抛出异常,因为我们试图传递一个未被接受的参数给构造函数。

4. 使用元类

通过定义一个元类,你可以在类的创建过程中控制实例的生成。

# 使用元类实现
class Meta(type):instance = {}def __new__(cls, name, base, dct):return super().__new__(cls, name, base, dct)def __call__(cls, *args, **kwargs):if cls not in cls.instance:cls.instance[cls] = super().__call__(*args, **kwargs)return cls.instance[cls]class Singleton3(metaclass=Meta):passa = Singleton3()
b = Singleton3()
print(a)
print(b)print(a == b)

5. 使用全局变量

在函数内部创建一个类的实例,并将其存储为全局变量,从而实现单例模式。

class _Singleton:pass_instance = _Singleton()def get_singleton():return _instance

6. 使用线程安全的单例模式

如果你的应用是多线程的,你可能需要确保单例模式在多线程环境下也是安全的。可以使用锁来确保只有一个线程可以创建实例。

#  使用线程安全的单例模式
from threading import Lockclass Singleton4:instance = Nonelock = Lock()def __new__(cls, *args, **kwargs):if not cls.instance:with cls.lock:if not cls.instance:cls.instance = super().__new__(cls, *args, **kwargs)return cls.instancea = Singleton4()
b = Singleton4()
print(a)
print(b)print(a == b)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/820645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能助手大比拼!5款热门思维导图软件细致评估!

思维导图是一种创造性的方法,集思广益,寻找不同想法之间的联系。如果你做得好,你可以为难题提出新的想法和解决方案,总结一篇文章或演示稿,让你的想法井然有序。在数字时代,纸质思维导图存在不能随意更改、…

java声明一个数组工具类ArrayTools

声明一个数组工具类ArrayTools,包含如下方法: int sum(int[] arr):求所有元素总和 int max(int[] arr):求所有元素的最大值 int indexOf(int[] arr, int value):查找value在arr数组中第一次出现的下标,如果…

arm64-v8a、armeabi-v7a、x86、x86_64

当我们去GitHub下载应用的时候是不是经常很懵逼,就像下图一样,粗看一下如此多安装包到底要选择下载哪个且每种安装包到底有哪差别?毕竟因为自己一无所知,有时便随意下载一个后,安装时却报『此版本与你的系统不兼容』的…

Python的pytest框架(1)--基本概念、入门

按基础到进阶的顺序,学习Python的pytest框架,本篇文章先讲一讲pytest的基本概念、入门使用规则。 目录 一、pytest基础知识 1、安装 2、pytest框架主要做了什么工作 二、pytest的规则约定、运行方式以及参数详解 1、编写测试用例 模块&#xff08…

Oracle 19c RAC 补丁升级 补丁回退

补丁升级流程 补丁升级 停止集群备份家目录 两节点分别操作 cd /u01/app/19.3.0/grid/bin/ crsctl stop crs tar -zcvf /u01/app.tar.gz /u01/app /u01/app/19.0.0/grid/bin/crsctl start crs 两节点OPatch替换 --- 表示 root 用户,$ 表示 Oracle 用户提示符&#…

ChatGPT改变你的论文写作方式

ChatGPT无限次数:点击直达 html ChatGPT改变你的论文写作方式 引言 随着人工智能技术的不断发展,ChatGPT这样的自然语言处理模型正逐渐成为学术界和商业领域的热门话题。本文将介绍如何利用ChatGPT改变传统的论文写作方式,提高写作效率和质量。 Ch…

PCB设计指南教程,设计一个在纸上和物理形式上都真实可靠的电路板

目录 一.前言 二.微调元件布置 三.合适放置电源,接地和信号走线 四.有效隔离

负荷预测 | Matlab基于TCN-GRU-Attention单变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于TCN-GRU-Attention单变量时间序列多步预测; 2.单变量时间序列数据集,采用前12个时刻预测未来96个时刻的数据; 3.excel数据方便替换,运行环境matlab2023及以…

无人棋牌室软硬件方案

先决思考 软件这一套确实是做一套下来,可以无限复制卖出,这个雀氏是一本万利的买卖。 现在肯定是有成套的方案,值不值得重做?为什么要重做? 你想达到什么效果?还是需要细聊的。 做这个东西难度不高&…

DNF手游攻略:萌新入坑大全!

玩DNF手游国服已经正式定档,离上线已经越来越近了,很多小伙伴对于装备打造以及附魔还不是特别了解。如果你还不知道装备要怎么附魔,不要担心,本篇攻略将为你全面解析全职业过渡和毕业附魔推荐。 ​ 一、物理职业附魔推荐 1. 武器…

1688推出跨境业务,用API自动对接商品货源

2023年底,出海圈迎来一则重磅消息:1688正式进军海外市场。这一决策引发了众多卖家的关注与疑惑,为何1688会在这个时候推出跨境版呢? 事实上,1688早已涉足跨境业务,拥有“跨境专供”板块,成为众…

ACM/NOI/CSP比赛

ACM、NOI、CSP这三项比赛均属于计算机科学与信息技术领域的竞赛,各自有着不同的定位、参赛对象及比赛形式。下面对这三项比赛进行详细介绍: ACM(ACM International Collegiate Programming Contest,ACM-ICPC) 概念&a…

【C++学习】C++IO流

这里写目录标题 🚀C语言的输入与输出🚀什么是流🚀CIO流🚀C标准IO流🚀C文件IO流 🚀C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取…

Cloudflare Workers 付费文档

定价 默认情况下,用户可以访问Workers免费计划。Workers免费计划包括对Workers、Pages Functions和Workers KV的有限使用。了解更多关于免费计划限制的信息。 Workers付费计划包括Workers、Pages Functions、Workers KV和Durable Objects的使用,每个账…

PCIE/CPCIE技术优势

PCI/CPCI技术缺点 1) 并行总线无法连接太多设备,总线扩展性比较差,线间干扰将导致系统无法正常工作; 2) 当连接多个设备时,总线有效带宽将大幅降低,传输速率变慢; 3) 为了降低成本和尽可能减少相互间的干扰…

MedSAM环境搭建推理测试

引子 之前分享过一篇SAM(感兴趣的,请移步Segment Anything(SAM)环境安装&代码调试_segment anything环境-CSDN博客)环境搭建&推理测试,虽然话说Segment Anything,但是原始模型对于一些…

一文详解MES、ERP、SCM、WMS、APS、SCADA、PLM、QMS、CRM、EAM及其关系

经常遇到很多系统,比如:MES、ERP、SCM、WMS、APS、SCADA、PLM、QMS、CRM、EAM,这些都是什么系统?有什么功能和作用?它们之间的关系是怎样的? 今天就一文详细分享给大家。 10大系统之间的关系 ERP 和其他…

网络层协议——IP协议

目录 IP协议 IP协议格式 分片与组装 网段划分 特殊IP地址 IP地址的数量限制 私有IP地址和公网IP地址 路由 路由表生成算法 IP协议 IP协议全称为“网际互连协议(Internet Protocol)”,IP协议是TCP/IP体系中的网络层协议。 在应用层我…

一例白加黑样本的分析

概述 这是一个典型的白加黑的恶意代码,原始样本是一个自解压文件,可能是钓鱼样本,使用了一个合法签名的exe加载一个恶意的dll,在内存中解密和运行恶意载荷,,创建启动项的方式很特别,没有传播功…

FreeRTOS_day1

1.总结keil5下载代码和编译代码需要注意的事项 下载代码前要对仿真进行设置 勾选后代码会立刻执行 勾选后会导致代码不能执行 写代码的时候要写在对应的begin和end之间,否则会被覆盖 2.总结STM32Cubemx的使用方法和需要注意的事项 ①打开软件,新建工程…