一、什么是单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
单例设计模式:
**目的:**让类创建的对象,在系统只执行唯一实例,即每一次执行类名.()返回的对象,内存地址是相同的,可以通过id(实例对象)来查看实例对象对应的内存空间地址。
应用场景: 音乐播放器、回收站对象、打印机对象…
二、通过装饰器实现单例模式
面试真题:
通过装饰器实现单例模式,只要任意一个类使用该装饰器装饰,
那么就会变成一个单例模式的类。
1.用函数方法实现:
def single(cls):cls.instance = Nonedef wrapper(*args, **kwargs):# 判断类是否有instance属性if not cls.instance:# 通过类创建对象,并保存为instance属性cls.instance = cls(*args, **kwargs)# 返回对象return cls.instancereturn wrapper
@single # A = single(A)
class A:pass
a1 = A()
a2 = A()
print(a1, a2)
运行结果:
<__main__.A object at 0x000001F125DE2D60> <__main__.A object at 0x000001F125DE2D60>
2.通过类来实现:
class single1:def __init__(self, cls):self.cls = clsself.cls.instance = Nonedef __call__(self, *args, **kwargs):if not self.cls.instance:self.cls.instance = self.cls(*args, **kwargs)return self.cls.instance
@single1
class B:pass
b1 = B()
b2 = B()
print(b1, b2)
运行结果:
<__main__.B object at 0x000001F125DE2DC0> <__main__.B object at 0x000001F125DE2DC0>
三、基于基于__new__方法实现
__new__方法:
使用类名()创建对象时,python的解释器首先会调用__new__方法为对象分配空间,然后在执行__init__初始话实例对象。
object提供的__new__静态方法,主要有2个作用:
- 在内存中为对象分配空间
- 返回对象的引用
Python解释器获取对象的引用后,将引用作为第一个参数,传递给__init__方法
注意点:
重写__new__方法一定要return super().new(cls), 否则python解释器得不到分配空间引用,就不会调用对象的初始化
__new__是一个静态方法,在调用时需要主动传递cls函数。
代码实现:
class A(object):__instance = Nonedef __new__(cls, *args, **kwargs):if cls.__instance is None:cls.__instance = super().__new__(cls)return cls.__instanceelse:return cls.__instancea1 = A()
a2 = A()
print("a1的id为:", id(a1))
print("a2的id为:", id(a2))
print("a1 is a2:", a1 is a2)
print(a1)
print(a2)
运行结果:
a1的id为: 2168943480688
a2的id为: 2168943480688
a1 is a2: True
<__main__.A object at 0x000001F8FF1B0F70>
<__main__.A object at 0x000001F8FF1B0F70>
解析如下:
目的: 让类创建的对象,在系统中只有唯一的一个实例
流程:
定义一个类属性,初始值是None, 用于记录单例对象的引用。
重写__new__方法
如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
返回类属性中记录的对象引用。