该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。比如:
class MusicPlayer(object):
def __init__(self):
print("播放器初始化")
# 创建播放器对象
player = MusicPlayer()
print(player)
输出:
播放器初始化
<__main__.MusicPlayer object at 0x0000020B7CC0A908>
可以看到在我们创建对象后,内存中有一块空间存放我们的对象。接下来我们在代码中加上__new__方法。
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
# 1.创建对象时,new方法会被自动调用
print("创建对象,分配空间")
def __init__(self):
print("播放器初始化")
# 创建播放器对象
player = MusicPlayer()
print(player)
输出:
创建对象,分配空间
None
???为什么内存地址为空了?并且__init__也没有执行?,其实重写__new__方法非常固定,一定要 return super().new(cls), 否则Python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法,并且还要注意:__new__是一个静态方法,在调用时需要主动传递 cls 参数
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
# 1.创建对象时,new方法会被自动调用
print("创建对象,分配空间")
# 2.为对象分配空间
return super().__new__(cls)
# 3.返回对象的引用
def __init__(self):
print("播放器初始化")
# 创建播放器对象
player = MusicPlayer()
print(player)
输出:
创建对象,分配空间
播放器初始化
<__main__.MusicPlayer object at 0x000002589EFCB668>
class MusicPlayer(object):
pass
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
输出:
<__main__.MusicPlayer object at 0x000001E65983A908>
<__main__.MusicPlayer object at 0x000001E659847EB8>
我们创建了一个音乐播放器的类,在两次调用中,可以看到有两个不同的内存地址,如果是单例模式那么应该是我们不管使用类名()创建多少个对象,都只有一个内存地址,我们改造一下。
class MusicPlayer(object):
# 记录第一个类被创建对象的引用
instance = None
def __new__(cls, *args, **kwargs):
# 1.判断类属性是否是空对象
if cls.instance is None:
# 2.调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance
pass
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
输出:
<__main__.MusicPlayer object at 0x000001A44D55B588>
<__main__.MusicPlayer object at 0x000001A44D55B588>
我们创建一个类属性,来记录第一个类创建时候的引用,然后再有创建的话就不会重新分配内存地址了。
在上面的代码中我们确实只有一个实例了,但是初始化方法是否只调用了一次呢?
class MusicPlayer(object):
# 记录第一个类被创建对象的引用
instance = None
def __new__(cls, *args, **kwargs):
# 1.判断类属性是否是空对象
if cls.instance is None:
# 2.调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance
def __init__(self):
print("初始化播放器")
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
输出:
初始化播放器
<__main__.MusicPlayer object at 0x000002559993BA58>
初始化播放器
<__main__.MusicPlayer object at 0x000002559993BA58>
在两次创建实例时,都调用了初始化方法,在实例化过程中,我们无法阻止初始化方法的执行,但是可以通过增加一个标记达到只执行一次。
class MusicPlayer(object):
# 记录第一个类被创建对象的引用
instance = None
# 记录是否执行过初始化方法
init_flag = False
def __new__(cls, *args, **kwargs):
# 1.判断类属性是否是空对象
if cls.instance is None:
# 2.调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance
def __init__(self):
# 1.判断是否执行过初始化动作
if MusicPlayer.init_flag:
return
# 2.如果没有执行过,再执行初始化动作
print("初始化播放器")
# 3.修改类属性标记
MusicPlayer.init_flag = True
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)
初始化播放器
<__main__.MusicPlayer object at 0x0000012EAE31B550>
<__main__.MusicPlayer object at 0x0000012EAE31B550>
我们用init_flag这个类属性去记录是否执行过初始化方法,默认False,当执行过初始化动作后,我们就把状态改为True,保证下一次创建是不再去执行初始化动作一遍。
继续保持学习,在于寒冬之中亦有光芒散放。