单例模式-Python示例

单例模式

单例模式(Singleton Pattern)是设计模式中一种创建型模式,广泛应用于软件开发中。一以下以故事化的方式,结合详细的技术讲解,介绍单例模式的背景、定义、适用场景,并提供python的示例代码。

故事1:皇帝的玉玺

在古代的龙国,皇帝是国家的唯一最高统治者,象征权力的玉玺也只有一块。无论多少大臣、多少事务需要盖章,所有人都必须使用同一块玉玺。皇帝下令:这块玉玺是独一无二的,不能有第二块!谁敢私自造玉玺,格杀勿论!
于是,为了确保玉玺的唯一性,皇帝派专人保管,任何人需要盖章时,都得向保管者申请使用这块玉玺。这样,全国上下都用同一块玉玺,保证了权力的统一性和一致性。
有一天,邻国的使者来访,带来了一个问题:如果多个大臣同时需要盖章,玉玺如何分配?皇帝想了想,决定让保管者记录玉玺的使用情况,确保每次只有一个人能拿到玉玺使用,其他人必须排队等待。这不仅保证了玉玺的唯一行,还避免了混乱。

这个故事中的玉玺,就是单例模式的完全体现:全局唯一、受控访问。

在古代的一个小村庄里,有一口古老的“智慧之井”,据说井水能赋予饮用者无穷的智慧。村民们都想喝到井水,但村长发现,如果每个人都随意打水,井水很快就会干涸。于是,村长宣布:全村只能有一个“水官”负责管理井水,每天只打一桶水,供大家享用。这个“水官”就是全村唯一的井水管理者,任何时候只有他能接触到井水,确保井水不会被滥用。

这个故事就像程序设计中的单例模式。在软件开发中,有些资源(如数据库连接、配置文件、线程池等),就像“智慧之井”,如果每次都创建新实例,会浪费资源或导致冲突。单例模式就像村里的“水官”,确保全局只有一个实例,统一管理资源。

单例模式是什么?

单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式的核心是控制对象的创建过程,确保系统中该类的对象始终只有一个。

解决什么样的问题

单例模式主要解决以下问题:

1、资源共享:避免多次创建对象导致的资源浪费(如数据库连接池、日志对象)

2、全局状态管理:需要一个全局唯一的对象来协调系统行为(如配置管理器、计数器)

3、控制并发访问:防止多个实例同时操作同一资源导致的数据不一致(如线程池)

4、减少系统开销:避免频繁创建和销毁对象,提升性能

适用场景

单例模式适用于以下场景

1、需要全局唯一实例的资源

  • 配置文件管理器:整个应用共享同一份配置
  • 日志记录器:统一管理日志输出
  • 数据库连接池:避免重复创建连接

2、需要控制资源访问

  • 线程池:统一管理线程资源
  • 缓存管理器:全局共享缓存数据

3、需要全局协调的场景

  • 计数器:记录系统中某些操作的次数
  • 状态管理器:维护系统的全局状态

优缺点

优点:

  • 节省资源:只创建一个实例,减少内存和系统开销
  • 全局访问点:提供统一的访问入口,方便管理
  • 严格控制实例:避免多实例带来的冲突或数据不一致

缺点

  • 单点故障:如果单例对象出现问题,可能影响整个系统
  • 难以测试:单例的全局状态可能导致单元测试复杂化
  • 违反单一职责原则:单例既负责自身逻辑,又负责实例管理
  • 扩展性差:难以继承或修改,因为单例通常是静态的

实现方式

单例模式有几种常见实现方式

  • 饿汉式:类加载时酒创建实例(线程安全,但可能浪费资源)
  • 懒汉式:需要时才创建实例(需考虑线程安全)
  • 双检锁:懒汉式的线程安全优化
  • 静态内部类:结合饿汉式和懒汉式的优点(Java常用)
  • 模块级单例:Python的模块天然支持单例

Python示例代码

以下展示几种Python实现单例模式的方式,并附上详细注释,为了贴合故事,代码以“皇帝的玉玺”作为类名

方式1:经典懒汉式(线程不安全)

class ImperialSeal:# 私有类变量,存储唯一实例_instance = Nonedef __new__(cls):# 如果实例不存在,则创建if cls._instance is None:cls._instance = super().__new__(cls)return cls._instancedef __init__(self):# 出事话只执行一次,防止重复初始化if not hasattr(self, '_initialized'):self._initialized = Trueself.seal_name = "Dragon Seal"def use_seal(self):print(f"Using the {self.seal_name} to stamp a decree!")# 测试代码
if __name__ == "__main__":seal1 = ImperialSeal()seal2 = ImperialSeal()# Trueprint(f"Same instance: {seal1 is seal2}")# Using the Dragon Seal to stamp a decree!seal1.use_seal()

说明

  • new方法控制实例创建,确保只有一个实例
  • init适用_initialized防止重复初始化_
  • 缺点:多线程环境下可能创建多个实例(线程不安全)

方式2:线程安全的懒汉式(使用锁)

import threading
from threading import Lockclass ImperialSeal:# 私有类变量,存储唯一实例_instance = None# 锁,用于线程安全_lock = Lock()def __new__(cls):with cls._lock:if cls._instance is None:cls._instance = super().__new__(cls)return cls._instancedef __init__(self):if not hasattr(self, '_initialized'):self._initialized = Trueself.seal_name = "Dragon Seal"def use_seal(self):print(f"Using the {self.seal_name} to stamp a decree!")def create_seal():seal = ImperialSeal()print(f"Created seal: {id(seal)}")# 测试代码
if __name__ == "__main__":threads = [threading.Thread(target=create_seal) for _ in range(5)]for t in threads:t.start()for t in threads:t.join()seal1 = ImperialSeal()seal2 = ImperialSeal()# Trueprint(f"Same instance: {seal1 is seal2}")# Using the Dragon Seal to stamp a decree!seal1.use_seal()

说明

  • 使用threading.Lock确保多线程环境下之创建一次实例
  • with cis.lock保证线程安全,但加锁可能影响性能
  • 适合多线程场景,如web服务器中的全局配置管理

方式3:Python模块单例

Python的模块本身是天然的单例,因为模块只加载一次。以下展示如何利用模块实现单例

# 单例模式 - 模块级单例实现
# 通过在模块级别定义全局唯一实例来实现单例模式class ImperialSeal:def __init__(self):self.seal_name = "Dragon Seal"  # 玉玺名称def use_seal(self):print(f"使用 {self.seal_name} 盖章于圣旨!")  # 使用玉玺盖章# 定义全局唯一实例
seal = ImperialSeal()# 测试代码
if __name__ == "__main__":# 直接引用模块中的 seal 实例,模拟多次导入seal1 = sealseal2 = sealprint(f"是否为同一实例: {seal1 is seal2}")  # 应输出 Trueseal1.use_seal()  # 输出: 使用 Dragon Seal 盖章于圣旨!

说明

  • Python模块在程序运行期间只加载一次,seal变量天然全局唯一
  • 简单高效,无需显式控制实例创建
  • 适合简单场景,但不适合需要复杂初始化逻辑的情况

故事续篇:玉玺的挑战

龙国的玉玺管理逐渐复杂,大臣们发现

  • 并发问题:多个大臣同时申请玉玺,导致盖章混乱(线程安全问题)
  • 测试麻烦:玉玺的全局性让模拟测试变的困难(单例的全局状态问题)
  • 扩展需求:邻国提议联合使用玉玺,但玉玺无法轻易扩展(单例扩展性差)

皇帝召集智囊团,决定

  • 使用“锁匠”(线程锁)确保玉玺一次只被一人使用(线程安全单例)
  • 编写“玉玺副本”用于测试(依赖注入替换单例)
  • 对于新需求,考虑“多玉玺模式”(非单例设计)

这个故事告诉我们:单例模式虽然简单有效,但需谨慎使用,避免滥用导致维护困难

适用场景举例

1、日志管理器:全局唯一的日志对象,确保日志写入一致

import logging
# logging 模块天然单例
logger = logging.getLogger("app")

2、数据库连接池

import pymysqlclass DBConnection:_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super().__new__(cls)cls._instance.conn = pymysql.connect(host="localhost",user="root",password="123456",database="test")return cls._instance

3、配置管理:全局读取配置,避免重复加载

class Config:_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super(Config, cls).__new__(cls)cls._instance.settings = {"api_key": "12345", "timeout": 30}return cls._instance

注意事项

  • 线程安全:多线程环境下,有限适用锁或模块级单例
  • 测试问题:单例的全局状态可能影响单元测试,建议结合依赖注入
  • 滥用风险:不宜将所有全局对象都用单例,可能导致单例过多,增加复杂性
  • Python特性:Python的模块机制天然支持单例,优先考虑模块级单例以简化代码

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

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

相关文章

啥是 SaaS

https://www.youtube.com/watch?vnpcL7oRZQlI这个视频讲了什么东西, 什么 idea?好的,这个视频内容非常棒,信息量很足。下面为你详细总结视频讲了什么,以及核心的 Idea 是什么。 视频核心 Idea 这个视频讲的是一位名叫 Leandro…

Spring Boot 工程启动以后,我希望将数据库中已有的固定内容,打入到 Redis 缓存中,请问如何处理?

在 Spring Boot 工程中,将数据库中的固定内容预先加载到 Redis 缓存中可以通过以下步骤实现。这里假设你已经配置好了 Spring Data Redis 和数据库(如 MySQL)的连接。 1. 添加依赖 首先,确保你的 pom.xml(Maven&…

springboot企业级项目开发之项目测试——集成测试!

集成测试 集成测试是指项目代码在单元测试完成后进行的第二阶段测试。集成测试的重点是在集成组件或单元之间交互时暴露缺陷,以保证不同模块之间相互调用的正确性。在Spring Boot的项目集成测试中,将测试Controller和Dao的完整请求处理。应用程序在服务…

HTML 媒体(Media)

HTML 媒体(Media) 引言 HTML 媒体元素是构成现代网页的重要组成部分,它允许我们在网页中嵌入各种类型的媒体内容,如音频、视频、图像等。这些元素不仅丰富了网页的视觉效果,还提升了用户体验。本文将详细介绍 HTML 媒…

轻量化分布式AGI架构:基于区块链构建终端神经元节点的互联网智脑

一、架构概述 该架构通过将终端设备(如手机、IoT设备)转化为神经元节点,结合区块链技术构建去中心化智能网络,形成“互联网智脑”。其核心在于突破传统AGI算力瓶颈,实现数据安全共享与价值分配。 1.1 关键特征 分布…

【知识图谱构建系列6】:借了张显卡先跑着

文章目录 前情提要mistral模型运行代码前情提要 之前咱对LLM4KGC的代码稍作修改,目标是用modelscope来下载模型。 现在这个代码终于能跑了。 前面咱说,我们的显卡只有6G的显存。现在呢,我也成功借到了A100的显卡。这下,咱可以先跑跑这个项目默认带的mistral模型。 mist…

从零开始手写redis(16)实现渐进式 rehash map

手写 Redis 系列 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? jav…

List、Queue、Deque、Stack常用方法总结

Java 中几个常见的线性数据结构的 方法总结与对比,包括: List(ArrayList、LinkedList)Queue(LinkedList、PriorityQueue)Deque(ArrayDeque、LinkedList)Stack(传统 Stac…

github为InfiniSynapse Docker提PR过程留档@Windows10

为InfiniSynapse Docker提了一个PR:修改阿里源为清华源,并不再安装PPA。 by skywalk163 Pull Request #1 chaozwn/infini_docker 整体操作 提PR的前置动作 先fork要提PR的项目git clone到本地用VSCode修改代码 提交PR git add . git commit -m &…

搭建加解密网站遇到的问题

本机向云服务器传输文件 用winscp 服务器在安装 SSH 服务时自动生成密钥对(公钥私钥) 为什么要有指纹验证? 防止中间人攻击(Man-in-the-Middle) 指纹验证打破这个攻击链: 小问题 安装python时 ./confi…

Docker高级管理--容器通信技术与数据持久化

第一节:容器通信技术 一:Docker 容器的网络模式 当项目大规模使用 Docker 时,容器通信的问题也就产生了。要解决容器通信问题,必须先了解很多关于网络的知识。Docker 的网络模式非常丰富,可以满足不同容器的通信要求&…

jsons.top工具之数组交集、去重

作为一名程序员,一款高效的 在线转换工具 (在线时间戳转换 计算器 字节单位转换 json格式化)必不可少!https://jsons.top 用js实现一个轻量级的集合运算工具,可以对数组、集合去重、求交并差集,找出两个集…

Vue3 + Tailwind CSS 后台管理系统教程

Vue3 搭配 Tailwind CSS 是构建现代后台管理系统的绝佳组合。Vue3 提供了高效的响应式框架,而 Tailwind CSS 则让样式编写变得快速且灵活。下面我将分步骤教你如何创建一个功能完整的后台管理系统。 第 1 步:创建项目 首先,我们需要使用 Vit…

ComfyUI遭“Pickai“C++后门攻击,全球700余台AI图像生成服务器沦陷

大规模AI基础设施遭遇定向攻击 网络安全研究机构XLab近日发现针对ComfyUI框架的活跃攻击活动。ComfyUI是当前广泛用于部署大型AI图像生成模型的开源框架。攻击者通过该框架漏洞植入名为Pickai的C后门程序,已导致全球近700台服务器失陷。中国国家网络安全通报中心于…

Unity_VR_如何用键鼠模拟VR输入_PICO项目配置

文章目录 [TOC] 一、创建项目1.直接创建VR核心模板(简单)2.创建3D核心模板导入XR包(并配置pico)(1)创建项目(2)导入PICO的SDK(3)启用 PICO XR 插件&#xff0…

站点天下--网站在线和SSL过期监控的可靠助手

简介 网站突然访问不了、HTTPS证书到期,如果不能及时发现,将蒙受损失~ 站点天下提供应用在线状态监控和SSL证书到期监控: 若访问不了或SSL证书即将到期,则立即发邮件通知!可以在线查看应用的在线状态和SSL证书到期时…

React setState原理

异步更新 原因 1设置为异步提升性能 如果setState每次调用直接执行,会造成 render 函数被频繁执行 ,页面重新被渲染 解决:异步批处理 2如果render函数未执行时,保证props和state一致性 拿到最新state的方法 法一:setState&…

汉代大模型:历史镜像与智能重构的深度对话

引言:当历史遇见人工智能 一件汉代陶俑的三维模型正通过增强现实技术向观众演绎农耕场景。这个看似寻常的文物活化案例,实则蕴含着人工智能与历史学交叉领域的前沿探索——汉代大模型。作为连接过去与未来的智能载体,汉代大模型不仅重构了我…

es向量检索里的efSearchc参数是干嘛用的

在Elasticsearch的向量检索中,ef_search(或efSearch)是控制HNSW近似最近邻(ANN)搜索精度与性能平衡的关键参数,其作用机制和影响如下: 🛠️ 一、核心作用 ef_search 限制底层图遍历…

Mac SSH终端操作工具 SecureCRT

SecureCRT Mac 是一款SSH终端工具,为计算专业人士提供高级会话管理工具。 也是一个功能强大且值得信赖的基于GUI的SHH和Telnet客户端,以及旨在提高工作效率并简化重复任务的终端仿真器。 借助SecureCRT mac版的帮助,您可以通过对ANSI&#…