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


以上是单例模式的应用方法与思路
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__是实例初始化方法,负责对已创建的实例进行属性初始化。下面为你详细解析这段代码的执行逻辑、输出结果,以及进一步的扩展(比如实现单例模式)。
代码执行流程与输出解析
-
调用
MusicPlayer()创建对象时,首先触发__new__方法:- 执行
print("创建对象,分配空间"),打印该字符串。 - 调用父类(
object)的__new__方法:super().__new__(cls),为MusicPlayer实例分配内存空间,返回实例的引用(instance)。 __new__方法返回实例引用,这个引用会作为self参数传递给__init__方法。
- 执行
-
接着触发
__init__方法:- 执行
print("播放器初始化"),打印该字符串。
- 执行
-
最后打印
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 自动传入实例 |
| 访问范围 | 类属性、类方法 | 实例属性、实例方法、类属性 |