Python设计模式 - 单例模式

定义

单例模式是一种创建型设计模式, 其主要目的是确保一个类只有一个实例, 并提供一个全局访问点来访问该实例。

结构

应用场景

  1. 资源管理:当需要共享某个资源时,例如数据库连接、线程池、日志对象等,可以使用单例模式确保所有的客户端都使用同一个资源实例,从而避免资源的浪费和不一致性。
  2. 配置信息:在应用程序中,可能会有一些全局配置信息需要在各个地方被访问和使用,例如系统配置、日志配置等,这时可以使用单例模式来存储和管理这些配置信息。
  3. 缓存管理:在需要缓存数据以提高性能的场景中,可以使用单例模式来管理缓存实例,确保所有地方都使用同一个缓存对象,避免数据不一致或者缓存混乱的问题。

优缺点

优点:

  1. 资源节约:单例模式确保一个类只有一个实例存在,可以节约系统资源,避免了多次创建相同类型的对象所带来的资源浪费。
  2. 全局访问点:单例模式提供了一个全局的访问点,使得可以在任何时候、任何地方都能够访问到该实例,方便了对象的访问和使用。
  3. 实例控制:由于单例模式只能创建一个实例,因此可以对实例进行严格的控制,例如可以限制实例的数量、延迟实例化等。

缺点:

  1. 对扩展性的限制:由于单例模式创建的实例是静态的,因此很难对其进行子类化或者扩展。如果需要在单例类的基础上添加新的功能,可能需要修改现有的代码,这会增加耦合性并且破坏了开闭原则。
  2. 职责过多:单例模式在一定程度上违背了单一职责原则,因为单例类既提供了业务方法,又提供了创建对象的方法,将对象的创建和对象本身的功能耦合在一起。

Java代码示例

饿汉式

饿汉式实现是在类定义时就创建单例对象,不管是否需要使用该对象。

java 复制代码
public class Singleton {
    // 在类加载时就创建好实例
    private static Singleton instance = new Singleton();

    // 私有化构造函数,防止外部实例化
    private Singleton() {}

    // 提供一个公共的静态方法返回实例
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式

懒汉式实现是在需要时才创建实例。

java 复制代码
public class Singleton {
    // 声明一个静态的实例变量,但不初始化
    private static Singleton instance;

    // 私有化构造函数,防止外部实例化
    private Singleton() {}

    // 提供一个公共的静态方法返回实例
    public static Singleton getInstance() {
        // 检查实例是否已经被创建,如果没有,才进行实例化
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

以上实现不是线程安全的,在多线程高并发访问时,要使用双重检查锁定来实现线程安全。

java 复制代码
public class Singleton {
    // 声明一个 volatile 类型的静态变量,确保多线程下的可见性
    private static volatile Singleton instance;

    // 私有化构造函数,防止外部实例化
    private Singleton() {}

    // 提供一个公共的静态方法返回实例
    public static Singleton getInstance() {
        // 第一次检查,如果实例为空,则进入同步块
        if (instance == null) {
            synchronized (Singleton.class) {
                // 第二次检查,再次判断实例是否为空,如果为空,则创建实例
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类(推荐)

使用静态内部类实现单例模式是一种常见的方式,这种方法利用了类加载的特性来保证懒加载和线程安全。

java 复制代码
public class Singleton {

    // 私有化构造方法,防止外部实例化
    private Singleton() {}

    // 静态内部类,用于实现懒加载和线程安全
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 对外提供获取单例对象的方法
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Python代码示例

使用模块

使用模块来实现饿汉式单例模式是一种简单而有效的方法。在 Python 中导入一个模块时,解释器会确保这个模块只被加载一次,因此可以利用这一点来实现单例模式。

python 复制代码
# singleton.py
class Singleton:
    pass

# 创建单例实例
singleton_instance = Singleton()


# main.py
from singleton import singleton_instance

# 使用单例实例
singleton_instance.some_attribute = "some_value"

使用装饰器

使用装饰器可以实现懒汉式单例模式,实例在第一次被请求时才会被创建。

python 复制代码
def singleton(cls):
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

@singleton
class Singleton:
    pass

# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()

使用类属性

使用类属性也可以实现懒汉式单例模式。

python 复制代码
class Singleton:
    _instance = None  # 类属性用于存储单例实例



    @classmethod
    def get_instance(cls, value):
        if not cls._instance:
            cls._instance = cls(value)
        return cls._instance

# 使用单例对象
singleton_instance1 = Singleton.get_instance()
singleton_instance2 = Singleton.get_instance()

也可以通过 new 方法来实现,同样是将实例对象存储到类属性中。

python 复制代码
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

  
# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()

使用元类(推荐)

使用元类可以更加灵活地控制类的创建过程,从而实现懒汉式单例模式。

python 复制代码
class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    pass

# 使用单例对象
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()

在多线程高并发访问时,要使用双重检查锁定来实现线程安全。

python 复制代码
from threading import Lock, Thread


class SingletonMeta(type):
    _instances = {}
    _lock = Lock()

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                if cls not in cls._instances:
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    pass

饿汉式单例类和懒汉式单例类的比较

  1. 资源利用率:饿汉式单例类在类被加载时创建实例,无论运行时是否使用该实例;懒汉式单例类在第一次使用时创建实例;所以懒汉式单例类资源利用率更高。
  2. 线程安全问题:饿汉式单例类在类被加载时创建实例,是线程安全的;懒汉式单例类需要增加双重检查锁定来实现线程安全;

参考

《设计模式的艺术》
单例设计模式 (refactoringguru.cn)
Python中的单例模式的几种实现方式的及优化 - 听风。 - 博客园 (cnblogs.com)

相关推荐
Kai HVZ43 分钟前
python爬虫----爬取视频实战
爬虫·python·音视频
古希腊掌管学习的神1 小时前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
m0_748244831 小时前
StarRocks 排查单副本表
大数据·数据库·python
B站计算机毕业设计超人1 小时前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
路人甲ing..1 小时前
jupyter切换内核方法配置问题总结
chrome·python·jupyter
游客5201 小时前
opencv中的常用的100个API
图像处理·人工智能·python·opencv·计算机视觉
每天都要学信号1 小时前
Python(第一天)
开发语言·python
凡人的AI工具箱2 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
咸鱼桨2 小时前
《庐山派从入门到...》PWM板载蜂鸣器
人工智能·windows·python·k230·庐山派
北京_宏哥2 小时前
python接口自动化(四十)- logger 日志 - 下(超详解)
python·前端框架·自动化运维