当前位置: 首页 > news >正文

单例模式-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的模块机制天然支持单例,优先考虑模块级单例以简化代码

相关文章:

  • 如何仅用AI开发完整的小程序<4>—小程序页面创建与删除
  • 【Linux】进程间多种通信方式对比
  • Flink Sink函数深度解析:从原理到实践的全流程探索
  • vscode+react+ESLint解决不引入组件,vscode不会报错的问题
  • 【设计模式】策略模式 在java中的应用
  • 魂斗罗ost 游戏全合集8GB
  • 应急推进器和辅助推进器诊断函数封装
  • Python爬虫实战:研究Ghost.py相关技术
  • RK3588/RK3576/RK3562、T113/T527 MIPI CSI调试思路
  • Windows防火墙指南大全:安全红线与科学替代方案
  • MongoDB:索引
  • 解锁n8n:开启工作流自动化的无限可能(5/6)
  • 一个免费的视频、音频、文本、图片多媒体处理工具
  • 16_设备树中的remote-endpoint演示基于视频字符设备Linux内核模块
  • 集群聊天服务器---muduo库的使用
  • 鲲鹏服务器创建Zookeeper镜像实例
  • 网络安全智能体:重塑重大赛事安全保障新范式
  • 《Go小技巧易错点100例》第三十六篇
  • TDengine 3.3.5.0 新功能——服务端查询内存管控
  • 【RocketMQ 生产者和消费者】- 消费者的订阅关系一致性
  • 网站设计速成/希爱力双效片副作用
  • 北京住房和城乡建设局门户网站/搜索优化指的是什么
  • 电子商务网站规划与设计/网络营销策划方案格式
  • 建设网站自学/昆明排名优化
  • 网站建设如何建/中国十大策划公司排名
  • html5网络公司网站模板/老铁外链工具