likes
comments
collection
share

一统江湖的设计模式:单例模式的魅力与实践

作者站长头像
站长
· 阅读数 10

前言

工作中一次发现一个偶现问题,去找开发,开发说“我这一块使用的单例模式不可能又问题”,那啥是单例模式呢?在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__方法获取单例实例,并提供了 infoerror 方法用于记录日志。

下面是使用实现单例模式方法三简单的示例代码:

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
'''

这是我在项目中常用的方式,这里在详细解释一下:

  1. singleDecorator 是一个装饰器函数,它接受一个类作为参数,并返回一个包装类的函数 wrapper
  2. singleDecorator 函数内部,定义了一个名为 __instance 的字典,用于保存每个类的单例实例。
  3. wrapper 函数是装饰器的包装函数,它接受任意数量的位置参数 *args 和关键字参数 **kwargs
  4. wrapper 函数内部,先判断类 cls 是否在 __instance 字典中。如果不在,则创建一个新的类实例,并将其保存到 __instance 字典中。
  5. 最后,返回 __instance 字典中对应的类实例。
  6. 使用 @singleDecorator 装饰器将 LogManager 类装饰为单例模式。
  7. 创建了两个 LogManager 类的实例 log_managerlog_manager2

解释为什么只输出一次 LogManager.__init__

在这段代码中,LogManager 类只有在第一次创建实例时才会调用 __init__ 方法。由于使用了单例模式,第一次创建实例后,后续的创建操作都会直接返回已经创建的实例,不会再次调用 __init__ 方法。

因此,在执行 log_manager = LogManager() 时,会输出 LogManager.__init__,而在执行 log_manager2 = LogManager() 时,并不会再次调用 __init__ 方法,所以没有输出。

这样可以确保整个项目中只有一个 LogManager 实例,并且在多次创建实例时能够使用同一个已存在的实例。

最后

本文介绍了Python中的单例模式概念、实现方式,并通过一个具体的项目实战案例展示了其实际应用。单例模式可以确保类只有一个实例,并提供全局访问点,对于需要控制唯一资源或避免频繁创建实例的场景非常有用。在实际项目中,通过合理地使用单例模式,我们可以提高代码的性能和可维护性。另外,方法二中提到metaclass,后续会写一篇文章详细介绍,这里看不懂没关系。而方法-和方法三中提到的__new____init__、装饰器这些知识点在之前的文章已经介绍过了,可以翻阅查看。