1.单例模式的设计
- 设计模式
设计模式是前人工作的总结和提炼,通常被人们广泛流传的设计模式都是针对某一特定问题的成熟解决方法,相当于针对 某一问题已经有了解决套路,我们只需要知道套路即可
使用设计模式是为了可重用代码,让代码容易被他人理解,保证代码可靠性
- 单例设计模式
目的 : 让类创建的对象,在系统中只有唯一的一个实例
每一次执行 类名()返回的对象内存地址是相同的
- 单例模式的应用场景:
音乐播放器 对象:播放10收歌,只需要一个播放器即可
打印机 对象: 打印10份文件,只需要一个打印机即可
显然设计单例模式就是希望无论调用几次类,只开辟一块类对象的内存。那么要开辟一块内存,需要知道中那个方法在调用时会自动开辟内存,下面的__new__方法就是为对象分配空间,然后对内存new方法的改造即可达到单例模式的效果
2.__new__方法
- 使用 类名() 创建对象时,python的解释器首先会调用 __new__方法为对象分配空间
- __new__是一个由object基类提供的内置的静态方法,主要作用有两个:
- 在内存中为对象分配空间
- 返回对象的引用
- Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
- 重写__new__方法一定要return super().__new__(cls)
- 否则Python的解释器得不到分配了空间对象引用,就不会调用对象的初始化方法
- 注意:__new__是一个静态方法,在调用时需要主动传递cls参数
new方法的重写代码:
class MusicPlayer:
def __new__(cls,*args,**kwargs):#new方法的重写
# 1.创建对象时,new 方法会被自动调用
print("创建方法,分配空间")
# 2.为对象分配空间
instance=super().__new__(cls)#调用父类new方法,new方法是静态方法,需要传入参数cls
# 3.返回对象的引用
return instance #必须要return,否则无法初始化
def __init__(self):
print("播放器初始化")
#创建播放器
player=MusicPlayer()
print(player)#打印None 因为没有return
3.python中单例模式
- 单例让类创建的对象,在系统中只有一个唯一的实例
- 定义一个属性类,初始值None,用于记录单例模式的引用
- 重写__new__方法
- 如果类属性 is None ,调用父类方法分配空间,并在类属性中记录结果
- 返回类属性中记录的对象引用
如果不是单例模式则定义几个对象,打印的地址都不相同如下图:
在创建对象时,无论调用多少次创建对象的方法,永远只是返回第一个创建对象的地址。
需要定义一个类属性instance,通过如下流程图即可完成单例模式的设计
代码:
class MusicPlayer:
# 记录第一个被创建对象的引用
instance=None
def __new__(cls,*args,**kwargs):
# 1.判断类属性是否是空对象
if cls.instance is None:
# 2.调用父类方法,为第一个对象分配空间
cls.instance=super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance
player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)
#结果:
#<__main__.MusicPlayer object at 0x0000000009B83CC0>
#<__main__.MusicPlayer object at 0x0000000009B83CC0>
上面的代码虽然new了一块内存,但是执行了多次初始化方法,那么如何只调用一次初始化方法
只执行一次初始化方法:
在每次使用 类名()创建对象时,Python的解释器会自动调用两个方法:
__new__ 分配空间 __init__ 对象初始化
但在上面的代码修改之后,每次都会得到第一次被创建的引用,但是初始化方法还会被再次调用
如何让初始化方法只执行一次:
1.定义一个类属性init_flag 标识是否执行过初始化动作,初始值为false
2.在__init__方法中,判断init_flag,如果为false就执行初始化动作
3.然后将init_flag 设置为true
4.这样再次调用__init__方法时,初始化动作就不会被再次执行了
class MusicPlayer:
# 记录第一个被创建对象的引用
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
# 如果没有执行过,在执行初始化动作
print("初始化播放器")
# 修改类属性的标记
MusicPlayer.init_flag=True
player1=MusicPlayer()
print(player1)
player2=MusicPlayer()
print(player2)