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

说明:

  • player1和player2是同一个实例(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就是假,程序不再继续向下执行,所以只执行了第一次的初始化,后续的就不再执行了)

cls与self的区别

特性 cls self
代表对象 类本身(类对象) 类的实例(实例对象)
适用方法 类方法、__new__方法 实例方法
传递方式 Python 自动传入类本身 Python 自动传入实例
访问范围 类属性、类方法 实例属性、实例方法、类属性
相关推荐
默_笙3 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
qq_426003963 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技3 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时3 天前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
奇思妙想聪明勤奋的小羊3 天前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
lpfasd1233 天前
2026年第38周GitHub趋势周报
python·科技·github