无论是在python代码中,还是面试中单例设计模式都是经常被问到和使用的,例如面试中会让你用代码实现单例模式分几种不同的方式,或者问你在平常工作中哪些地方有用到单例设计模式,然后深入探讨。
在本文中我将针对这两个问题来回答和用python代码来编写我们的单例模式。
首先,我们要了解什么是单例模式--官方解释是:确保一个类只有一个实例(也就是类的对象),并且提供一个全局的访问点(外部通过这个访问点来访问该类的唯一实例)。通俗的说就是,无论类实例化多少次,都只有一个相同的对象产生,并且可以通过一个具柄去访问这个这个唯一实例。
其次,我们平时代码中碰到是用单例模式设计的代码有哪些呢?
- 应用框架的配置文件config(如flask,django框架的配置文件)
- 线程池,数据库连接池的创建
- 应用程序的日志应用
这些应用都有一个共性那就是:他们都是全局共享的对象不会发生变化,不希望重复创建,这样有利于节省空间和减少性能消耗。
python实现单例模式的几种方式分别有:
一、类方法实现
class SingleType:
_instance = None
@classmethod
def instance(cls):
if not cls._instance:
cls._instance = SingleType()
return cls._instance
for i in range(100):
print(SingleType.instance())
看一下打印结果:
返回的都是同一个对象,实现了我们说的,无论调用多少次,都只返回同一个实例,但是这在多线程的环境中,可能会存在返回多个实例,我们实验一下。
import threading
import time
class SingleType:
_instance = None
def __init__(self):
time.sleep(1)
super().__init__()
@classmethod
def instance(cls):
if not cls._instance:
cls._instance = SingleType()
return cls._instance
def task():
obj = SingleType.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task).start()
print(SingleType.instance())
这是多线程环境测试的代码结果如下:
可以看到在多线程的环境中,我们产生了两个2个对象,这就违背了我们的单例原则了,那怎么解决它呢,答案是加锁。
import threading
import time
class SingleType:
_instance = None
_lock = threading.Lock()
def __init__(self):
time.sleep(1)
super().__init__()
@classmethod
def instance(cls):
if not cls._instance:
with SingleType._lock:
if not cls._instance:
cls._instance = SingleType()
return cls._instance
def task():
obj = SingleType.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task).start()
加锁后我们看运行结果:
这样就成功解决在多线程环境下单例的问题了, 至于问什么在最外层还要加一个if判断,是为了提高程序的性能。
二、装饰器模式实现
这种实现方式在我上一片将装饰器一文中是有提及的,在这我们再实现一次
def decorator(cls):
def wrap(*args, **kwargs):
if not cls._instance:
cls._instance = cls()
return cls._instance
return wrap
@decorator
class SingleType:
_instance = None
def __init__(self):
super().__init__()
for i in range(10):
obj = SingleType()
print(obj)
当然这里也会出现多线程创建多个实例的问题,可以参照上一个例子自行去实现多线程的版本。
三、使用__new__方法实现
在说__new__方法实现之前,我们需要有个前提知识,我们对象在实例话时会调用__init__方法,但是在调用__init__方法之前,类会先调用__new__方法,__new__方法中决定实例化怎样的对象(可以是调用object的__new__方法正常接着实例话,可以生成其他类的实例等)具体知识可以自行去补充,本文主要讲单例模式。
class SingleType:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
super().__init__()
for i in range(10):
obj = SingleType()
print(obj)
查看运行结果:
四、使用元类实现单例模式
前提知识: 实例化对象时方法调用顺序是: type类的__cal__方法,其中会调用__new__方法和__init__方法
import threading
class SingleType(type):
lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not cls._instance:
with SingleType.lock:
if not cls._instance:
cls._instance = super().__call__(*args, **kwargs) # 这个地方也可以替换成 obj = cls.__new__(cls) --> cls.__init__(onj) --> cls._instance=obj
return cls._instance
class Test(metaclass=SingleType):
_instance = None
for i in range(10):
obj = Test()
print(obj)
在代码注释部分的作用相当于自己实现type.__call__的功能。
至此python实现单例设计模式所有的实现方式已经讲完,既然说的是设计模式就要把设计模式的八大准则贴出来,时常警醒自己。