python进阶(29)单例模式
初识单例模式
单例模式含义
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例
存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
实现单例模式思路:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
单例模式优点
- 由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间
- 全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用
- 单例可长驻内存,减少系统开销
单例模式缺点
- 单例模式的扩展是比较困难的
- 赋于了单例以太多的职责,某种程度上违反单一职责原则
- 单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试
- 单例模式在某种情况下会导致“资源瓶颈”
单例模式应用
- 生成全局唯一的序列号
- 访问全局复用的唯一资源,如磁盘、总线等
- 单个对象占用的资源过多,如数据库等
- 系统全局统一管理,如Windows下的Task Manager
- 网站计数器(同步控制)
Python实现单例模式
使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
常见的场景即logging模块,写好一个日志类模块后,并实例化一个logger, 整个项目就调用这个logger
// logging_module.py
class LoggerMgr(object):
def foo(self):
pass
mylogger = LoggerMgr()
__new__方法实现
class Singleton:
__instance = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self, name):
if Singleton.__init_flag:
print("进行初始化")
self.name = name
Singleton.__init_flag = False
使用装饰器来获取单例对象
def singleton(cls):
__instance = {}
def getinstance(*args, **kwargs):
if cls not in __instance:
__instance[cls] = cls(*args, **kwargs)
return __instance[cls]
return getinstance
@singleton
class MyClass:
a = 1
我们定义了一个装饰器 singleton
,它返回了一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls
作为 key
,cls(*args, **kwargs)
作为 value
存到 instances
中,否则,直接返回 instances[cls]
。
使用metaclass元类创建单例
元类(metaclass)可以控制类的创建过程,它主要做三件事:
- 拦截类的创建
- 修改类的定义
- 返回修改后的类
class Singleton(type):
__instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls.__instances:
cls.__instances[cls] = super().__call__()
return cls.__instances[cls]
class MyClass(metaclass=Singleton):
def __init__(self):
self.blog = "blog"