一统江湖的设计模式:单例模式的魅力与实践
前言
工作中一次发现一个偶现问题,去找开发,开发说“我这一块使用的单例模式不可能又问题”,那啥是单例模式呢?在python中又该如何实现单例呢?项目实战中如何运用单例呢?这片文章将详细阐述这些疑问。
什么是单例模式
确保一个类只有一个实例,并提供一种全局访问该实例的机制。在单例模式中,类的实例被限制为仅能创建一个,并且所有的访问都通过这个唯一实例进行。它可以防止多个实例的创建,避免资源的浪费,并提供一种简单的全局访问方式。
设计思想
有一些类,你希望他的实例是唯一的。 单例模式就是保证一个类有且只有一个对象(实例)的一种机制。
实现方式
1.重写__new__
和__init__
方法
class Singleton:
__instance = None
__isFirstInit = False
def __new__(cls, name):
if not cls.__instance:
Singleton.__instance = super().__new__(cls)
return cls.__instance
def __init__(self, name):
if not self.__isFirstInit:
self.__name = name
Singleton.__isFirstInit = True
def getName(self):
return self.__name
jenny = Singleton("Jenny")
kimi = Singleton("Kimi")
print(jenny.getName(), kimi.getName()) # Jenny Jenny
print("id(jenny):", id(jenny), "id(kimi):", id(kimi)) # id(jenny): 140576084614784 id(kimi): 140576084614784
print("jenny == kimi", jenny == kimi) # jenny == kimi True
这段代码,我们定义了一个静态的__instance
类变量,用来存放Singleton的对象,__new__
方法每次返回同一个__instance
对象(若未初始化,则进行初始化)。 __isFirstInit
作用确保只对__instance
对象进行一次初始化。
2.自定义metaclass方法
class Singleton(type):
def __init__(cls, what, bases=None, dict=None):
super().__init__(what, bases, dict)
cls._instance = None
def __call__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class CustomClass(metaclass=Singleton):
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
tony = CustomClass("Tony")
karry = CustomClass("karry")
print(tony.getName(), karry.getName()) # Tony Tony
print("id(tony):",id(tony), "id(karry):", id(karry)) # id(tony): 140202384711296 id(karry): 140202384711296
print("tony == karry:",tony == karry) # tony == karry: True
这段代码,我们定义了metaclass(Singleton)来控制对象的实例化过程
3.装饰器的方法
def singleDecorator(cls, *args, **kwargs):
instance = {}
def wrapperSingleton(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wrapperSingleton
@singleDecorator
class Singleton:
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
tony = Singleton("Tony")
karry = Singleton("karry")
print(tony.getName(), karry.getName()) # Tony Tony
print("id(tony):",id(tony), "id(karry):", id(karry)) # id(tony): 140540364732160 id(karry): 140540364732160
print("tony == karry:",tony == karry) # tony == karry: True
装饰器的实质就是对传进来的参数进行补充,可以在不对原有的类做任何代码变动的前提下增加额外的功能,使用装饰器可以装饰多个类。
项目实战:日志管理器的单例模式应用
在许多项目中,日志记录是必不可少的功能。为了确保日志的统一管理和全局访问,我们可以使用单例模式来创建一个日志管理器。
下面是使用实现单例模式方法一简单的示例代码:
import logging
class LogManager:
__instance = None
__isFirstInit = False
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self):
# 初始化日志配置
if not self.__isFirstInit:
print('LogManager.__init__')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
self.__isFirstInit = True
def info(self, message):
logging.info(message)
def error(self, message):
logging.error(message)
log_manager = LogManager()
log_manager.info("This is an info log.")
log_manager.error("This is an error log.")
在上述代码中,LogManager
类实现了单例模式。通过 重写__new__
方法获取单例实例,并提供了 info
和 error
方法用于记录日志。
下面是使用实现单例模式方法三简单的示例代码:
import logging
def singleDecorator(cls, *args, **kwargs):
__instance = {}
def wrapper(*args, **kwargs):
if cls not in __instance:
__instance[cls] = cls(*args, **kwargs)
return __instance[cls]
return wrapper
@singleDecorator
class LogManager:
def __init__(self):
print('LogManager.__init__')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def info(self, message):
logging.info(message)
def error(self, message):
logging.error(message)
log_manager = LogManager()
log_manage2 = LogManager()
print(id(log_manager), id(log_manage2))
'''
LogManager.__init__
4457912928 4457912928
'''
这是我在项目中常用的方式,这里在详细解释一下:
singleDecorator
是一个装饰器函数,它接受一个类作为参数,并返回一个包装类的函数wrapper
。- 在
singleDecorator
函数内部,定义了一个名为__instance
的字典,用于保存每个类的单例实例。 wrapper
函数是装饰器的包装函数,它接受任意数量的位置参数*args
和关键字参数**kwargs
。- 在
wrapper
函数内部,先判断类cls
是否在__instance
字典中。如果不在,则创建一个新的类实例,并将其保存到__instance
字典中。 - 最后,返回
__instance
字典中对应的类实例。 - 使用
@singleDecorator
装饰器将LogManager
类装饰为单例模式。 - 创建了两个
LogManager
类的实例log_manager
和log_manager2
。
解释为什么只输出一次 LogManager.__init__
:
在这段代码中,LogManager
类只有在第一次创建实例时才会调用 __init__
方法。由于使用了单例模式,第一次创建实例后,后续的创建操作都会直接返回已经创建的实例,不会再次调用 __init__
方法。
因此,在执行 log_manager = LogManager()
时,会输出 LogManager.__init__
,而在执行 log_manager2 = LogManager()
时,并不会再次调用 __init__
方法,所以没有输出。
这样可以确保整个项目中只有一个 LogManager
实例,并且在多次创建实例时能够使用同一个已存在的实例。
最后
本文介绍了Python中的单例模式概念、实现方式,并通过一个具体的项目实战案例展示了其实际应用。单例模式可以确保类只有一个实例,并提供全局访问点,对于需要控制唯一资源或避免频繁创建实例的场景非常有用。在实际项目中,通过合理地使用单例模式,我们可以提高代码的性能和可维护性。另外,方法二中提到metaclass
,后续会写一篇文章详细介绍,这里看不懂没关系。而方法-和方法三中提到的__new__
、__init__
、装饰器这些知识点在之前的文章已经介绍过了,可以翻阅查看。
转载自:https://juejin.cn/post/7268830497651752979