一、什么是单例模式?
保证整个系统中一个类只有一个对象的实例,实现这种功能的方式就叫单例模式。
单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例,如果实例不存在,会创建一个实例;如果已存在就会返回这个实例。因为单例是一个类,所以你也可以为其提供相应的操作方法,以便于对这个实例进行管理。
python中,一个类创建对象实例是通过调用父类object的 new(cls)方法来创建对象的,因此我们可以通过重写 new(cls)方法去实现类只创建一个实例,下面咱们就先简单介绍以下这个python的魔术方法之一–new方法。
二、new方法
对象的创建: **new()**方法
new()方法用于定义创建对象时执行的操作
new方法对init方法的影响
先看以下代码:
class Man:
def __init__(self,name,age):
print("init方法被执行......")
self.name = name
self.age = age
man1 = Man("小明",18)
执行结果:
我们可以看到,在创建对象时init方法会自动执行。
下面我们写上new方法:
class Man:
def __new__(cls, *args, **kwargs): # 重写new方法
print("new方法被执行......") # 增加功能的操作
def __init__(self,name,age):
print("init方法被执行......")
self.name = name
self.age = age
man1 = Man("小明",18)
执行结果:
现在可以看到,创建对象后init里面的方法并没有被执行,我们现在可以知道两点:
1.__new__()方法创建对象时自动运行。
2.覆盖object类中的__new__方法后创建对象将执行覆盖后的方法
那么怎么消除重写new方法后对init方法的影响呢? 实际上,object类中的__new__()方法完成对象创建过程中的内存空间申请, 对象属性初始化等一系列的操作。而我们在增加了一些功能之后,只需要再让它返回object的new方法就行了,程序示例:
class Man: # object
def __new__(cls, *args, **kwargs): # 重写new方法
print("new方法被执行......") # 增加功能的操作
# 调用object的new方法
instance = object.__new__(Man) # 分配一个内存空间
return instance
def __init__(self,name,age):
print("init方法被执行......")
self.name = name
self.age = age
man1 = Man("小明",18)
执行结果:
三、尝试用new方法创建单例类
所谓的单例,顾名思义就是单个实例即控制一个类只能产生一个对象,下面给出尝试代码:
我们知道创建一个普通的类,可创建多个对象,如:
class Man():
pass
man1 = Man()
man2 = Man()
print(id(man1))
print(id(man2))
执行结果:
我们可以根据它们的内存地址不会相同来验证创建的是否是同一个对象。下面尝试用new方法控制只开辟一次内存空间, 那么就会创建出一个对象,程序示例:
class TheMan:
instance = None
def __new__(cls, *args, **kwargs):
# 控制只开辟一次内存空间, 那么就会创建出一个对象
if cls.instance == None:
cls.instance = object.__new__(TheMan) # 开辟一个内存空间,
return cls.instance
man1 = TheMan() # 一个对象被创建
man2 = TheMan() # 同一个对象
print(id(man1))
print(id(man2))
执行结果:
上面的功能已经实现,仔细想想上面的类变量instance在类外部可以随意修改,这样将会很不安全,怎么解决呢?很简单把它设置成私有就行了,完善程序:
class TheMan:
__instance = None
def __new__(cls, *args, **kwargs):
# 控制只开辟一次内存空间, 那么就会创建出一个对象
if cls.__instance is None:
cls.__instance = object.__new__(TheMan) # 开辟一个内存空间,
return cls.__instance
man1 = TheMan() # 一个对象被创建
man2 = TheMan() # 同一个对象
print(id(man1))
print(id(man2))
这里提一小点,就是那个if 判断语句,虽然我们用==也可以实现,但是我们都知道,
== 判断是两个值是否相同, is 判断的是两个内存地址是否相同
严格意义上来说,is比较的才是内存地址,所以用is更严谨。