Python:用于有效对象管理的单例模式
1. 写在前面
在本文中,我们将介绍一种常用的软件设计模式 —— 单例模式。
通过示例,演示单例创建,并确保该实例在整个应用程序生命周期中保持一致。同时探讨它在 Python 中的目的、益处和实际应用。
关键点:
1、单例模式只有一个实例存在;
2、单例模式必须自己创建自己的唯一实例;
3、单例模式是一种软件设计模式,而不是专属于某种编程语言的语法;
公众号: 滑翔的纸飞机
现在让我们开始吧!
2. Python 单例模式
那什么是单例模式?
这是个非常简单的问题,基本上就是当我们有一个类时,我们只能实例化该类的一个实例对象,无论你是要优化资源使用、配置数据,还是要为我们的程序优化某个全局只读数据,该模式都提供了一个清晰有效的解决方案。
2.1 实现
因为有不同的方式可以创建。这里我将向你展示几种简单的方法。
2.1.1 使用 __init__
在这里,我将定义一个 Singleton
类并定义一个方法getInstance()
。我们的想法是,无论我在哪里调用,它都会返回 Singleton.getInstance()
的特定实例。
考虑,无论是否已创建类实例,都将返回特定实例对象。因此,在这里使用类变量,跟踪实例是否已创建。
class Singleton:# 类变量 __instance 将跟踪唯一的对象实例__instance = None@staticmethoddef getInstance():if Singleton.__instance == None:Singleton()return Singleton.__instance
现在看起来当然还不是一个单例,接下去通过__init__()
方法(类似于构造函数),在Singleton
对象实例化时被调用。该对象会将类变量设置为对象实例。__init__()
全局只应被调用一次,并且由该类中的方法调用(getInstance()
),保证__instance
类变量赋值之后不允许再此赋值。
def __init__(self):if Singleton.__instance != None:raise Exception("Singleton object already created!")else:Singleton.__instance = self
现在,让我们创建实例验证:
在s1 情况下会调用__init__()
创建实例;
在s2 情况下返回与s1相同实例;
s1 = Singleton.getInstance()
print(s1)
s2 = Singleton.getInstance()
print(s2)
而且如果我们在 s1 上设置属性,s2 也会有相同的值,因为它们都指向同一个对象。
s1.x = 5
print(s2.x)
它将打印以下输出
<__main__.Singleton object at 0x10e24fdf0>
<__main__.Singleton object at 0x10e24fdf0>
5
完整代码示例:
"""
@Time:2023/9/15 01:18
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""class Singleton:# 类变量 __instance 将跟踪唯一的对象实例__instance = Nonedef __init__(self):if Singleton.__instance != None:raise Exception("Singleton object already created!")else:Singleton.__instance = self@staticmethoddef getInstance():if Singleton.__instance == None:Singleton()return Singleton.__instanceif __name__ == '__main__':s1 = Singleton.getInstance()print(s1)s2 = Singleton.getInstance()print(s2)s1.x = 5print(s2.x)
2.1.2 使用 __call__
& metaclass
__call__()
:Python中,只要在创建类时定义了__call__()
方法,这个类就是可调用对象。
针对__call__()
使用参考示例:
class Father:def __call__(self):print('My call() function')fat=Father()
fat()输出:My call() function
很神奇不是,实例对象也可以像函数一样作为可调用对象来用。
接下去通过__call__
实现单例,完整代码示例如下:
"""
@Time:2023/9/15 01:26
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""class Singleton(type):_instances = {}def __call__(cls, *args, **kwargs):if cls not in cls._instances:cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)return cls._instances[cls]class Singleton1(metaclass=Singleton):passif __name__ == '__main__':s1 = Singleton1()print(s1)s2 = Singleton1()print(s2)s1.x = 5print(s2.x)
输出:
<__main__.Singleton1 object at 0x1120c8280>
<__main__.Singleton1 object at 0x1120c8280>
5
2.1.3 使用 __new__
简单介绍下__new__()
:只要是面向对象的编程语言,类的实例化都一定包含两个步骤:
(1)在内存中创建对象,即开辟一块内存空间来存放类的实例(Instance);
(2)初始化对象,即给实例的属性赋予初始值,例如全部填空;
在 python 中,第一步由 __new__
函数负责,第二步由 __init__
函数负责。
在这种方法中,我们将使用__new__()
,让它检查类变量以查看实例是否已创建,如果没有,我们就创建它,无论是否需要创建,我们都会返回实例。
class Singleton:# 类变量 __instance 将跟踪唯一的对象实例__instance = Nonedef __new__(cls):if (cls.__instance is None):cls.__instance = super(Singleton, cls).__new__(cls)return cls.__instance
因此,当第一次创建单例对象时,它会运行__new__
,当判断__instance
为 None,则创建对象并赋值至__instance
类变量。下次运行时,发现__instance
不是 None 已被设置,于是将返回 Singleton.__instance
这里通过调用超类的方法来实际返回对象实例本身,然后再返回它的__new__。
s1 = Singleton()
print(s1)
s2 = Singleton()
print(s2)# 同样,如果在 s1 上设置一个属性,s2将具有相同的值,因为它们都引用同一个对象
s1.x = 5
print(s2.x)
它将打印以下输出
<__main__.Singleton object at 0x10607beb0>
<__main__.Singleton object at 0x10607beb0>
5
完整示例:
"""
@Time:2023/9/16 23:04
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""class Singleton:# 类变量 __instance 将跟踪唯一的对象实例__instance = Nonedef __new__(cls):if (cls.__instance is None):cls.__instance = super(Singleton, cls).__new__(cls)return cls.__instanceif __name__ == '__main__':s1 = Singleton()print(s1)s2 = Singleton()print(s2)# 同样,如果在 s1 上设置一个属性,s2将具有相同的值,因为它们都引用同一个对象s1.x = 5print(s2.x)
2.1.4 装饰器
关于装饰器读者自行查阅其他博文;
本例照旧通过 __instance = {}
来跟踪实例对象。使用类地址作为键,实例作为值,每次创造实例时,检查该类是否存在实例,存在的话直接返回该实例即可,否则新建一个实例并存放在字典中。
函数装饰器示例:
"""
@Time:2023/9/16 23:29
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""def singleton(cls):__instance = {}def inner():if cls not in __instance:__instance[cls] = cls()return __instance[cls]return inner@singleton
class Cls(object):def __init__(self):print('My name Cls')if __name__ == "__main__":cls1 = Cls()print(cls1)cls1.x = 5cls2 = Cls()print(cls2)print(cls2.x)
输出:
My name Cls
<__main__.Cls object at 0x10f284160>
<__main__.Cls object at 0x10f284160>
5
类装饰器示例:
"""
@Time:2023/9/16 23:39
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""class Singleton(object):def __init__(self, cls):self._cls = clsself._instance = {}def __call__(self):if self._cls not in self._instance:self._instance[self._cls] = self._cls()return self._instance[self._cls]@Singleton
class Cls(object):def __init__(self):print('My name Cls')if __name__ == "__main__":# print('=======使用1=======')cls1 = Cls()print(cls1)cls1.x = 5cls2 = Cls()print(cls2)print(cls2.x)# print('=======使用2=======')# cls1 = Singleton(Cls)# cls2 = Singleton(Cls)# print(cls1)# print(cls2)
输出:
My name Cls
<__main__.Cls object at 0x10ede0850>
<__main__.Cls object at 0x10ede0850>
5
3. 使用案例
单例经常与设计模式结合使用,以解决最常见的问题,包括缓存、日志、配置设置和线程池。
案例1:
"""
@Time:2023/9/16 23:09
@Author:'jpzhang.ht@gmail.com'
@Describe:
"""class Logger:_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super(Logger, cls).__new__(cls)cls._instance._initialize()return cls._instancedef _initialize(self):self.log_file = open("log.txt", "a")def log(self, message):self.log_file.write(message + "\n")self.log_file.flush()def close(self):self.log_file.close()class LoggerFactory:def create_logger(self):return Logger()# 使用方法
if __name__ == "__main__":logger_factory = LoggerFactory()logger1 = logger_factory.create_logger()logger2 = logger_factory.create_logger()logger1.log("This is a log message from logger1")logger2.log("This is a log message from logger2")logger1.close()logger2.close()
在本例中,创建了一个采用单例模式的日志类Logger
。它确保只创建一个类实例。LoggerFactory类
是一个工厂类,可创建Logger类的实例。
log.txt:This is a log message from logger1
This is a log message from logger2
运行代码后,可以看到两个对象(logger1、logger2),实际是Logger类的同一个实例。这样可以确保日志记录只使用一个日志文件。
案例2:
线程安全是实现单例模式时的一个重要考虑因素,因为多个线程可能会尝试同时访问或创建类的实例。如果没有适当的同步,这可能会导致创建多个实例,从而违反单例原则。
import threading
import randomclass Singleton:_instance = Nonedef __init__(self):self.value = random.randint(1, 10)def __new__(cls):if cls._instance is None:cls._instance = super(Singleton, cls).__new__(cls)return cls._instancedef create_singleton(index):s = Singleton()print(f"Singleton instance created by thread {threading.current_thread().name}: {s} and value: {s.value}\n")# 模拟多个线程同时创建单例
def problem_case():threads = []for i in range(5):thread = threading.Thread(target=create_singleton, args=(i,))threads.append(thread)thread.start()for thread in threads:thread.join()# 使用锁来确保线程安全
class ThreadSafeSingleton:__instance = None__lock = threading.Lock()def __init__(self):self.value = random.randint(1, 10)@classmethoddef get_instance(cls):if not cls.__instance:with cls.__lock:if not cls.__instance:cls.__instance = cls()return cls.__instancedef create_thread_safe_singleton(index):s = ThreadSafeSingleton.get_instance()print(f"Singleton instance created by thread {threading.current_thread().name}: {s} and value: {s.value}\n")def thread_safe_case():threads = []for i in range(5):thread = threading.Thread(target=create_thread_safe_singleton, args=(i,))threads.append(thread)thread.start()for thread in threads:thread.join()if __name__ == "__main__":print("Problem case (without thread safety):")problem_case()print("\nThread-safe case:")thread_safe_case()
在上例中,ThreadSafeSingleton
使用线程锁类创建了一个锁对象,我们可以用它来同步对类变量的访问。然后定义一个类方法,首先检查是否已经创建了该类的实例。如果没有,它会获取锁,并创建该类的新实例。
备注:获取锁后再次校验是否已经创建了该类的实例。
输出:
Problem case (without thread safety):
Singleton instance created by thread Thread-10: <__main__.Singleton object at 0x102881760> and value: 4Singleton instance created by thread Thread-7: <__main__.Singleton object at 0x102881760> and value: 3Singleton instance created by thread Thread-6: <__main__.Singleton object at 0x102881760> and value: 10Singleton instance created by thread Thread-8: <__main__.Singleton object at 0x102881760> and value: 9Singleton instance created by thread Thread-9: <__main__.Singleton object at 0x102881760> and value: 4Thread-safe case:
Singleton instance created by thread Thread-11: <__main__.ThreadSafeSingleton object at 0x102874a60> and value: 2Singleton instance created by thread Thread-12: <__main__.ThreadSafeSingleton object at 0x102874a60> and value: 2Singleton instance created by thread Thread-13: <__main__.ThreadSafeSingleton object at 0x102874a60> and value: 2Singleton instance created by thread Thread-14: <__main__.ThreadSafeSingleton object at 0x102874a60> and value: 2Singleton instance created by thread Thread-15: <__main__.ThreadSafeSingleton object at 0x102874a60> and value: 2
不难看出,上锁后符合单例原则。
3. 最后
实现该模式的每种方法都有自己的优缺点,选择哪种方法可能取决于具体的用例。虽然该模式在某些情况下很有用,但它也可能带来一些缺点,例如使代码更难测试和维护。
总之,单例设计模式是管理应用程序中类的单个实例的强大工具。不过,在使用时应谨慎,考虑系统的具体要求以及对可维护性、可测试性和并发性的潜在影响。
感谢您花时间阅读文章
关注公众号不迷路:)
相关文章:
Python:用于有效对象管理的单例模式
1. 写在前面 在本文中,我们将介绍一种常用的软件设计模式 —— 单例模式。 通过示例,演示单例创建,并确保该实例在整个应用程序生命周期中保持一致。同时探讨它在 Python 中的目的、益处和实际应用。 关键点: 1、单例模式只有…...
【TCP】滑动窗口、流量控制 以及拥塞控制
滑动窗口、流量控制 以及拥塞控制 1. 滑动窗口(效率机制)2. 流量控制(安全机制)3. 拥塞控制(安全机制) 1. 滑动窗口(效率机制) TCP 使用 确认应答 策略,对每一个发送的数…...
Xilinx FPGA管脚约束语法规则(UCF和XDC文件)
文章目录 1. ISE环境(UCF文件)2. Vivado环境(XDC文件) 本文介绍ISE和Vivado管脚约束的语句使用,仅仅是管脚和电平状态指定,不包括时钟约束等其他语法。 ISE使用UCF文件格式,Vivado使用XDC文件&…...
服务网格和CI/CD集成:讨论服务网格在持续集成和持续交付中的应用。
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
代码随想录训练营第56天|583.两个字符串的删除操作,72.编辑距离
代码随想录训练营第56天|583.两个字符串的删除操作,72.编辑距离 583.两个字符串的删除操作文章思路代码 72.编辑距离文章思路代码 总结 583.两个字符串的删除操作 文章 代码随想录|0583.两个字符串的删除操作 思路 如果不按照编辑距离考虑的话,只需要…...
【JDK 8-Lambda】3.1 Java高级核心玩转 JDK8 Lambda 表达式
一、 什么是函数式编程 ? 二、 什么是lambda表达式? 1. 先看两个示例 A.【创建线程】 B.【数组排序-降序】 2. lambda表达式特性 A. 使用场景(前提): B. 语法 (params) -> expression C. 参数列表 D. 方法体 F. 好处 一、 什么是函数式编…...
【C#】XML的基础知识以及读取XML文件
最近在学读取文件 目录 介绍特点结构XML的语法规则XML 命名规则 C#操作XML新建读取第一种第二种第三种 读取属性 介绍 XML (可扩展标记语言,eXtensible Markup Language) 是一种标记语言,它被设计用来传输和存储数据。 特点 可扩展性:由于…...
Immutable.js简介
引子 看一段大家熟悉的代码 const state {str: wwming,obj: {y: 1},arr: [1, 2, 3] } const newState stateconsole.log(newState state) // truenewState和state是相等的 原因: 由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存…...
C语言进阶教程(位操作和进制数的表示)
文章目录 前言一、左移和右移二、清除对应的位为0和设置对应的位为11.设置对应的位为12.清除对应的位为0 三、进制数的表示四、& ^ | ~总结 前言 本篇文章给大家讲解一下C语言中的位操作,在嵌入式中位操作是经常需要使用的,那么下面就让我们来学习一…...
Loguru:功能强大、简单易用的Python日志库
文章目录 Loguru:Python的日志库安装 Loguru基本用法配置 Loguruadd() 语句remove() 语句设置日志文件保留日志的等级设置控制台日志显示等级Loguru:Python的日志库 Loguru 是一个功能强大、简单易用的日志库,可以让 Python 的日志记录变得更加轻松。它提供了丰富的功能和配…...
idea之maven的安装与配置
我们到maven的官网里下载maven,地址:https://maven.apache.org/download.cgi下载完成后解压即可配置环境变量 此电脑–>右键–>属性–>高级系统设置–>环境变量–>系统变量(S)–>新建一个系统变量 变量名&…...
【最新面试问题记录持续更新,java,kotlin,android,flutter】
最近找工作,复习了下java相关的知识。发现已经对很多概念模糊了。记录一下。部分是往年面试题重新整理,部分是自己面试遇到的问题。持续更新中~ 目录 java相关1. 面向对象设计原则2. 面向对象的特征是什么3. 重载和重写4. 基本数据类型5. 装箱和拆箱6. …...
面试:经典问题解决思路
1. 秒杀系统架构 参考:秒杀系统架构优化思路 2. 如何防止订单重复提交 重复提交原因: 一种是由于用户在短时间内多次点击下单按钮,或浏览器刷新按钮导致。另一种则是由于Nginx或类似于SpringCloud Gateway的网关层,进行超时重试造成的。 方案…...
CG MAGIC分享3ds Max卡顿未保存处理方法有哪些?
3ds Max进行建模、渲染这一系列过程中,大家使用中都会遇到各种原因导致软件卡顿或崩溃是很常见的情况。 可以说卡机没关系,可是卡顿发生时,如果之前的工作没有及时保存,可能会导致数据的丢失和时间的浪费。这就是最让人烦躁的了&…...
[python 刷题] 238 Product of Array Except Self
[python 刷题] 238 Product of Array Except Self 题目: Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements of nums except nums[i]. The product of any prefix or suffix of nums is guar…...
UG NX二次开发(C#)-计算直线到各个坐标系轴向的投影角度
文章目录 1、前言2、需求分析3、NXOpen方法实现3.1 创建基准坐标系3.2 然后计算直线到基准坐标系的轴向角度3.3 代码调用4、测试效果为:1、前言 最近有个粉丝问我如何计算直线到坐标系各个轴向的角度,这里用UG NX二次开发(C#)实现。当然,这里的内容是经验之谈,如果有更好的…...
C# ComboBox 和 枚举类型(Enum)相互关联
C# ComboBox 和 枚举类型(Enum)相互关联 目的 在C# Winform面板上的ComboBox选择项,由程序填写某个Enum的各个枚举项目。 在运行中读取ComboBox的选择项,返回Enum数值。 非编程方法 低阶做法可以在winform设计窗口手动填写,但是不会自动跟…...
Linux CentOS7 tree命令
tree就是树,是文件或文件名输出到控制台的一种显示形式。 tree命令作用:以树状图列出目录的内容,包括文件、子目录及子目录中的文件和目录等。 我们使用ll命令显示只能显示一个层级的普通文件和目录的名称。而使用tree则可以树的形式将指定…...
软件设计模式系列之九——桥接模式
1 模式的定义 桥接模式是一种结构型设计模式,它用于将抽象部分与其实现部分分离,以便它们可以独立地变化。这种模式涉及一个接口,它充当一个桥,使得具体类可以在不影响客户端代码的情况下改变。桥接模式将继承关系转化为组合关系…...
构造函数的调用规则
#include <iostream> #include <string> using namespace std; class person{ public:int m_age; // person(){ // cout<<"默认构造的调用"<<endl; // } // person(int age){ // m_ageage; // cout<<"有参构造的调用"<…...
第十章:枚举类与注解
10.1:枚举类的使用 当需要定义一组常量时,建议使用枚举类(前提:类的对象只有有限个,确定的) eg: 星期:Mondey、.....、Sunday 性别:Man、.....、Woman 线程状态ÿ…...
ChatGPT:字符串操作问题——提取包含括号的字符串中的题干内容
ChatGPT:字符串操作问题——提取包含括号的字符串中的题干内容 String title p.text().split(“(”)[0];为什么会报错 ChatGPT: 在这段代码中,您正在使用Java处理一个字符串(假设是HTML或文本),尝试将其分…...
jvm中对象创建、内存布局以及访问定位
对象创建 Java语言层面,创建对象通常(例外:复制、反序列化)仅仅是一个new关键字即可,而在虚拟机中,对象(限于普通Java对象,不包括数组和Class对象等)的创建又是怎样一个过…...
C基础-操作符详解
操作符分类: 算数操作符: - * / % //算数操作符 // int main() // { // // /除法 1.整数除法(除号两端都是整数) 2浮点数除法,除号的两端只要有一个小数就执行小数除法 // // 除法中,除数为0 // int a 7 / 2; /…...
时序预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元时间序列预测
时序预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元时间序列预测 目录 时序预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元时间序列预测。…...
【深度学习实验】线性模型(五):使用Pytorch实现线性模型:基于鸢尾花数据集,对模型进行评估(使用随机梯度下降优化器)
目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入库 1. 线性模型linear_model 2. 损失函数loss_function 3. 鸢尾花数据预处理 4. 初始化权重和偏置 5. 优化器 6. 迭代 7. 测试集预测 8. 实验结果评估 9. 完整代码 一、实验介…...
ADB底层原理
介绍 adb的全称为Android Debug Bridge,就是起到调试桥的作用。通过adb我们可以在Eclipse/Android Studio中方便通过DDMS来调试Android程序,说白了就是debug工具。adb是android sdk里的一个工具, 用这个工具可以直接操作管理android模拟器或者真实的and…...
etcd之读性能主要影响因素
1、Raft模块-线性读ReadIndex-节点之间的RTT延时、磁盘IO 线性读时Follower节点首先会向Raft 模块发送ReadIndex请求,此时Raft模块会先向各节点发送心跳确认,一半以上节点确认 Leader 身份后由leader节点将已提交日志索引 (committed index) 封装成 Rea…...
【Stable Diffusion】安装 Comfyui 之 window版
序言 由于stable diffusion web ui无法做到对流程进行控制,只是点击个生成按钮后,一切都交给AI来处理。但是用于生产生活是需要精细化对各个流程都要进行控制的。 故也就有个今天的猪脚:Comfyui 步骤 下载comfyui项目配置大模型和vae下载…...
Ansys Zemax | 如何建立二向分色分光镜
分光镜(Beam splitter)可被运用在许多不同的场合。一般而言,入射光抵达二向分色分光镜(dichroic beam splitter)时,会根据波长的差异产生穿透或反射的现象。这篇文章将说明如何在OpticStudio的非序列模式(non-sequential mode)中建立二向分色分光镜&…...
企业如何登录建设银行香港网站/郑州网站制作工具
2016年蓝桥杯B组C/C 点击查看2016年蓝桥杯B组省赛试题(无答案版) 第一题:煤球数目 题解 有一堆煤球,堆成三角棱锥形。具体: 第一层放1个, 第二层3个(排列成三角形), 第三…...
十堰网站建设公司0719web/seo基础教程视频
iptables/netfilter详解一、前言1.防火墙(Firewall)是隔离工具;工作于主机或网络的边缘,对经由的报文根据预先定义的规则(识别条件)进行检测, 对于能够被规则匹配到的报文实行某预定义的…...
dede网站重新安装/产品推广方案要包含哪些内容
3.6.4 地理信息索引 地理信息索引分为两类:2D平面索引,2DSphere球面索引。在2D索引里面基本上能够保存的信息都是坐标,而且坐标保存的就是经纬度坐标。 范例:定义一个shop的集合 db.shop.insert({loc : [10, 10]}) # loc表示坐…...
广告创意设计说明/seo优化便宜
1 JavaScript变量作用域 1.1 函数作用域 没有块作用域:即作用域不是以{}包围的,其作用域完成由函数来决定,因而if /for等语句中的花括号不是独立的作用域。 如前述,JS的在函数中定义的局部变量只对这个函数内部可见,称…...
中电云主机怎样登入创建的网站/能让手机流畅到爆的软件
1.Spring容器的实例化 1.1. 用构造器去实例化bean <!-- id/name属性用于执行bean的名称 用于从spring中获取这个对象, class用于指定bean的类型,会自动调用无参数构造器创建对象 --><beanid"calendarobj1"class"java.util.GregorianCalendar" /&…...
下沙经济开发区建设局网站/北京网站快速排名优化
struts核心控制器 在服务器启动时调用 init()方法,读取xml中常量信息→将xml配置文件中的信息,以一定格式封装到javabean中,所以修改了xml文件就需要重启服务器 之后执行dofilter方法 首先解析http请求→到javabean中寻找→找到后通过action…...