python:单例模式

首先说一下,查询了这么多单例模式的讲解,还是bilibili的这个视频讲解最清楚、简洁、实用且到位bilibili单例模式

https://www.bilibili.com/video/BV1cK4y1n7DC/?spm_id_from=333.1391.0.0&p=4&vd_source=58cc5704c1268f656a4d51980dac6aa7

以上是单例模式的应用方法与思路

复制代码
class MusicPlayer(object):

    def __new__(cls, *args, **kwargs):
        # 1.创建对象时,new方法会被自动调用
        print("创建对象,分配空间")
        # 2.为对象分配空间(调用父类的new方法)
        instance = super().__new__(cls)
        # 3.返回对象的引用
        return instance

    def __init__(self):
        print("播放器初始化")

#创建播放器对象
player = MusicPlayer()
print(player)

以上Python 代码展示了单例模式__new__方法的基础使用,__new__是 Python 中用于创建实例的静态方法(特殊方法),负责为对象分配内存空间,而__init__是实例初始化方法,负责对已创建的实例进行属性初始化。下面为你详细解析这段代码的执行逻辑、输出结果,以及进一步的扩展(比如实现单例模式)。

代码执行流程与输出解析

  1. 调用MusicPlayer()创建对象时,首先触发__new__方法

    • 执行print("创建对象,分配空间"),打印该字符串。
    • 调用父类(object)的__new__方法:super().__new__(cls),为MusicPlayer实例分配内存空间,返回实例的引用(instance)。
    • __new__方法返回实例引用,这个引用会作为self参数传递给__init__方法。
  2. 接着触发__init__方法

    • 执行print("播放器初始化"),打印该字符串。
  3. 最后打印player对象

    • 输出对象的内存地址(格式类似<__main__.MusicPlayer object at 0x0000021Fxxxxxx>)。

最终输出结果

复制代码
创建对象,分配空间
播放器初始化
<__main__.MusicPlayer object at 0x0000021Fxxxxxx>  # 地址会随环境变化

关键知识点:__new____init__的区别

特性 __new__ __init__
作用 创建实例,分配内存空间 初始化实例,为实例设置属性
调用时机 实例创建时最先调用 __new__返回实例后调用
参数 第一个参数是类(cls 第一个参数是实例(self
返回值 必须返回实例引用(否则__init__不执行) 无返回值(None

扩展:利用__new__实现单例模式

单例模式的核心是一个类只能创建一个实例 。通过重写__new__方法,我们可以控制实例的创建逻辑,确保始终返回同一个实例。修改后的代码如下:

复制代码
class MusicPlayer(object):
    # 类属性,用于保存唯一的实例引用
    instance = None

    def __new__(cls, *args, **kwargs):
        # 判断是否已创建实例
        if cls.instance is None:
            # 未创建则分配空间
            cls.instance = super().__new__(cls)
        # 已创建则直接返回已有实例
        return cls.instance

    def __init__(self):
        print("播放器初始化")

# 创建多个对象,验证是否为同一个实例
player1 = MusicPlayer()
player2 = MusicPlayer()
print(f"player1与player2是否为同一对象: {player1 is player2}")

输出结果

复制代码
播放器初始化
播放器初始化
player1与player2是否为同一对象: True

说明

  • player1player2是同一个实例(is判断为True),但__init__被调用了两次(实例化player1时调用了第一次初始化init,实例化player2时第二次调用了init)。如果需要让__init__只执行一次,可以在__new__中增加额外的判断标记。

进阶:单例模式且__init__只执行一次

复制代码
class MusicPlayer(object):
    instance = None
    init_flag = False  # 标记是否已初始化

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        if not MusicPlayer.init_flag:
            print("播放器初始化")
            MusicPlayer.init_flag = True  # 初始化后将标记设为True

player1 = MusicPlayer()
player2 = MusicPlayer()
print(f"player1与player2是否为同一对象: {player1 is player2}")

输出结果

复制代码
播放器初始化
player1与player2是否为同一对象: True

此时,无论创建多少个MusicPlayer实例,都只会执行一次初始化逻辑,且所有实例指向同一个内存地址。(第一次实例化的时候,将标志init_flag=true置为真了,第二次实例化的时候,if not init_flag就是假,程序不再继续向下执行,所以只执行了第一次的初始化,后续的就不再执行了)

clsself的区别

特性 cls self
代表对象 类本身(类对象) 类的实例(实例对象)
适用方法 类方法、__new__方法 实例方法
传递方式 Python 自动传入类本身 Python 自动传入实例
访问范围 类属性、类方法 实例属性、实例方法、类属性
相关推荐
SuperEugene8 小时前
前端 utils 工具函数规范:拆分 / 命名 / 复用全指南,避开全局污染等高频坑|编码语法规范篇
开发语言·前端·javascript
xin_yao_xin8 小时前
PaddleOCR系列——《文本检测、文本识别》模型训练
人工智能·python·paddlepaddle·ppocr
2401_833197738 小时前
用Python制作一个文字冒险游戏
jvm·数据库·python
古城小栈8 小时前
Go 底层代码的完整分类
开发语言·后端·golang
耳冉鹅8 小时前
Go无锁共享内存环形缓冲区设计
开发语言·golang
万粉变现经纪人8 小时前
如何解决 pip install cx_Oracle 报错 未找到 Oracle Instant Client 问题
数据库·python·mysql·oracle·pycharm·bug·pip
sw1213898 小时前
使用Plotly创建交互式图表
jvm·数据库·python
2301_810160958 小时前
如何为开源Python项目做贡献?
jvm·数据库·python
若惜8 小时前
selenium自动化测试web自动化测试 框架封装Pom
前端·python·selenium
计算机安禾8 小时前
【C语言程序设计】第36篇:二进制文件的读写
c语言·开发语言·c++·算法·github·visual studio code·visual studio