一. 什么是单例模式
单例模式 (Singleton Pattern) 是一种常用的软件设计模式 (Deaign pattern), 提供了在软件开发过程中面临的一些问题的最佳解决方案
而单例模式的主要目的是确保某一个类只有一个实例存在, 即一个类多次实例的结果指向同一个对象, 用于节省内存
如果我们从配置文件中读取配置来进行实例化, 在配置相同的情况下, 就没有必要重复产生对象来浪费内存了
二.单例模式的优缺点
1.单例模式的优点
- 由于单例模式要求在全局类只有一个实例, 因而可以节省比较多的内存空间
- 全局只有一个接入点, 可以进行更好的进行数据同步控制, 避免多重占用
- 单例可以常驻内存, 减少内存开销
2.单例模式的缺点
- 单例模式的扩展比较困难
- 赋于了单例以太多的职责,某种程度上违反单一职责原则
- 单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试
- 单例模式在某种情况下会导致 "资源瓶颈"
三.单例模式的应用场景示例
- 生成全局惟一的序列号
- 访问全局复用的惟一资源,如磁盘、总线等
- 单个对象占用的资源过多,如数据库等
- 系统全局统一管理,如Windows下的Task Manager
- 网站计数器
四.实现单例模式的五种常用方式
1.使用模块导入
前面我们介绍过模块导入只有首次导入会执行模块文件, 后面的导入都是直接使用第一次导入的结果, 因为在模块第一次导入时会生成一个 .pyc
文件, 后面导入直接加载它, 既然如此, 那么 Python 的模块就是一个天然的单例模式
- test.py 文件内容
class Singletan:
NAME = "北极星"
PASSWD = "275242996"
AGE = 22
SEX = "man"
info = Singletan()
- 执行文件正确演示
from test import info # 第一次导入执行test文件生成 .pyc 文件
from test import info # 第二次导入直接加载 .pyc 文件
obj1 = info
obj2 = info
print(obj1 is obj2) # True
- 执行文件错误演示
from test import Singletan # 第一次导入执行test文件生成 .pyc 文件
from test import Singletan # 第二次导入直接加载 .pyc 文件
info1 = Singletan()
info2 = Singletan()
print(info1 is info2) # False
2.使用类装饰器来实现
def Singletan(cls):
__instance = None # 定义一个常量, 用来存对象, 以后每次都返回这个对象
def wrapper(*args,**kwargs):
nonlocal __instance # 声明内层的"__instance"是外层函数的变量,不声明报错
if not __instance: # 对象不存在,将类的实例赋值给"__instance"
__instance = cls(*args,**kwargs)
# 这里是进行赋值操作,但如果没有声明成外层函数变量, 机器会识别成你是在定义变量,在定义之前引用会报错
return __instance # 对象已经存在时直接将对象返回
return wrapper
@Singletan # Singletan(Info)---->返回wrapper = Info
class Info:
def __init__(self):
...
obj1 = Info()
obj2 = Info()
print(obj1 is obj2) # True
3.使用类的绑定方法实现
class Singletan:
__instance = None # 设置变量存放对象
NAME = "shawn"
PASSWD = "275242996"
@classmethod
def get_obj(cls):
if not Singletan.__instance: # 判断是否对象存在
Singletan.__instance = cls() # 不存在实例出一个赋值
return Singletan.__instance # 存在直接返回这个对象
obj1 = Singletan.get_obj()
obj2 = Singletan.get_obj()
print(obj1 is obj2) # True
- 当使用了类的绑定方法实现单例后, 正常实例出来的对象不是单例
obj3 = Singletan()
obj4 = Singletan()
print(obj3 is obj4) # False
4.基于 __new__ 方法实现单例
class Singletan:
__instance = None # 设置变量存放对象
NAME = "shawn"
AGE = 22
def __new__(cls, *args, **kwargs):
if not cls.__instance: # 判断是否对象存在
cls.__instance = super().__new__(cls) # 不存在就实例出一个并赋值
return cls.__instance # 存在直接返回这个对象
obj1 = Singletan()
obj2 = Singletan()
print(obj1 is obj2) # True
5.通过元类实现
class SingletanType(type):
__instance = None # 设置变量存放对象,Singletan中找不到会跑这来找
def __call__(self, *args, **kwargs):
if not self.__instance: # 判断是否对象存在
self.__instance = self.__new__(self) # 不存在就实例出一个并赋值
# SingletanType.__instance = self.__new__(self) 直接设置元类的属性,类中找不到会到元类找(了解)
return self.__instance # 存在直接返回这个对象
class Singletan(metaclass=SingletanType):
NAME = "shawn"
AGE = 22
obj1 = Singletan() # 触发元类的 __call__
obj2 = Singletan()
print(obj1 is obj2) # True