单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场
单例模式的设计思想:
1.饿汉式:先有对象,如果要获取对象,直接把对象返回
采用:空间换取时间
2.懒汉式:只有在获取对象的时候,会首先判断对象是否存在
如果对象不存在,创建一个对象病返回,如果对象存在则直接返回该对象
采用:时间换取空间
python中不支持先创建对象,后在定义类的方式,因此python中没有用饿汉式实现单例模式的代码,只存在懒汉式实现单例模式,下面的重写__new__方式以及利用metaclass方法和闭包都是通过懒汉式的思想实现。一些其他的语言可以利用饿汉式的设计思想实现单例模式
【优点】
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
【缺点】
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用
单例模式的简单理解
1 单例模式 只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等
2 单例的缺点 就是不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
用单例模式,就是在适用其优点的状态下使用
使用__new__方法实现单例模式,代码如下:
class Car:
#创建单例标识
instance = None
#因为只创建一个对象,所以判断是否存在对象
isCreate = False
def __new__(cls,brand,color,wheel):
#创建对象,产生对象,判断标识是否存在
if Car.instance == None:
Car.instance = super().__new__(cls)
return Car.instance
#初始化属性数据
def __init__(self,brand,color,wheel):
# 根据Car.isCreate判断是否已经实例化对象,若第一为False,则认定为Car类为进行初始化操作,初始化操作进行之后,
# Car.isCreate则改变为True
if Car.isCreate == False:
self.brand = brand
self.color = color
self.wheel = wheel
#创建属性之后修改成已被创建
Car.isCreate = True
def show(self):
print(self.brand,self.color,self.wheel)
c1 = Car("大奔","黑色",4)
c1.show()
print(id(c1))
c2 = Car("奥迪","银色",4)
c2.show()
print(id(c2))
下图为程序运行结果,两次运行的结果一致,实现单例
利用metaclass方法与闭包实现单例模式,代码如下:
def signten(classname,bases,dicts):
# 设置标志,判断是否初始化
_instance = None
# 返回的是类对象
s = None
def get_instance(name,age):
nonlocal s
if s == None:
# 实例化,元类的使用
# classmate,bases,dicts为元类type的三个参数
_instance = type(classname,bases,dicts)
# s为类_instance的对象
s = _instance(name,age)
return s
# 闭包必须返回参数,参数为方法名,不可返回方法名()
# 引用函数,并非调用函数,返回的是函数对象
return get_instance
# 此时的User为方法,并非类,User()这时是在调用函数,并非类
class User(metaclass=signten):
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f"name:{self.name},age:{self.age}"
# print(User)
# 上面语句的结果如下,返回的是一个函数,而类
# <function signten.<locals>.get_instance at 0x0000027FB0920268>
# 通过函数Uesr(),并返回get_instance()函数的返回值
u = User("王",23)
u1 = User("郭",15)
print(u is u1)
程序代码运行如下:结果和利用__new__方式一样,但是区别在于:metaclass方法的使用改变了程序的运行结构,而闭包的存在,则延长了变量的存活周期。