Python中实现单例的几种方式小结

 更新时间:2026年04月05日 09:11:05   作者:zzwq.  
单例模式是一种常用的设计模式,本文就来介绍一下Python中实现单例的几种方式小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要控制资源(如数据库连接、日志记录器、配置管理等)的场景下非常有用。Python 作为一门动态语言,提供了多种灵活的方式来实现单例模式。本文将详细介绍几种常见的实现方法,并分析各自的优缺点。

一、单例模式简介

单例模式的核心要点:
 1. 一个类只能创建一个实例。
 2. 该实例必须由类自身负责创建。
 3. 必须向整个系统提供这个实例。

在 Python 中,由于对象的创建和初始化过程相对透明,实现单例需要一些技巧。下面我们逐一探讨。

二、Python实现单例的几种方式

1. 模块级单例

Python 的模块天然就是单例的:当模块第一次被导入时,会生成一个模块对象,随后再次导入同一模块时,Python 会直接返回缓存的模块对象。利用这个特性,我们只需将需要单例的类或对象定义在模块中,然后导入即可。

示例:

# singleton_module.py
class SingletonClass:
    def __init__(self):
        self.value = 0

    def do_something(self):
        print(f"Doing something, value = {self.value}")

# 创建一个全局实例
instance = SingletonClass()

在其他地方使用时:

from singleton_module import instance

# 无论导入多少次,instance 都是同一个对象
print(instance is instance)  # True

优点:

  • 实现简单,无需额外代码。
  • 线程安全(模块导入时 Python 会加锁)。

缺点:

  • 无法延迟实例化(模块导入即创建)。
  • 无法控制实例化的具体时机(如需要参数传递)。

2. 使用类装饰器

通过类装饰器,我们可以包装一个类,使其只能生成一个实例。

示例:

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 MyClass:
    def __init__(self, name):
        self.name = name

# 测试
a = MyClass("Alice")
b = MyClass("Bob")
print(a is b)   # True
print(a.name)   # Alice
print(b.name)   # Alice

优点:

  • 使用装饰器,语法简洁。
  • 可以接受初始化参数(但仅第一次有效)。

缺点:

  • 装饰后的类实际变成了函数,isinstance(a, MyClass) 会返回 False,可能破坏类型检查。
  • 多线程环境下需要额外处理线程安全。

3. 使用元类

元类(metaclass)是 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 MyClass(metaclass=SingletonMeta):
    def __init__(self, name):
        self.name = name

# 测试
a = MyClass("Alice")
b = MyClass("Bob")
print(a is b)   # True
print(a.name)   # Alice
print(b.name)   # Alice

优点:

  • 更符合面向对象设计,类仍是真正的类。
  • 可控制类的创建和实例化过程;线程安全问题可以自行添加锁。

缺点:

  • 代码相对复杂,需要对元类有一定了解。
  • 在多线程下需要加锁保证线程安全(上述代码未加锁,实际使用时可添加)。

线程安全版本:

import threading

class ThreadSafeSingletonMeta(type):
    _instances = {}
    _lock = threading.Lock()

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

4. 重写 __new__ 方法

Python 中对象的创建由 __new__ 方法控制,我们可以通过重写 __new__ 来限制实例化。

示例:

class Singleton:
    _instance = None

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

    def __init__(self, name):
        # 注意:每次调用 __init__ 都会执行,可能导致属性被覆盖
        self.name = name

# 测试
a = Singleton("Alice")
b = Singleton("Bob")
print(a is b)   # True
print(a.name)   # Bob
print(b.name)   # Bob

优点:

  • 实现直观,是 Python 中最常见的单例实现方式。
  • 可以控制实例创建时机。

缺点:

  • __init__ 每次都会执行,可能导致状态被重置(如上例中 name 被覆盖)。
  • 可以通过在 __init__ 中添加标志位避免重复初始化,但增加了复杂度。

改进版:

class Singleton:
    _instance = None
    _initialized = False

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

    def __init__(self, name):
        if not self._initialized:
            self.name = name
            self._initialized = True

5. 使用 @classmethod 的类方法

通过类方法提供实例的获取方式,将构造函数私有化(Python 中无法真正私有,但可以用约定)。

示例:

class Singleton:
    _instance = None

    def __init__(self):
        # 防止外部直接调用 __init__
        raise RuntimeError("Use get_instance() to create instance")

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls.__new__(cls)
            # 可以在此处进行初始化
            cls._instance._init()
        return cls._instance

    def _init(self):
        # 实际初始化代码
        self.value = 0

# 使用
s1 = Singleton.get_instance()
s2 = Singleton.get_instance()
print(s1 is s2)  # True

优点:

  • 完全控制了实例的获取方式。
  • 避免了 __init__ 重复执行的问题。

缺点:

  • 使用方式不够自然(需要调用 get_instance 而不是直接 Singleton())。
  • 仍可通过 Singleton.__new__(Singleton) 创建新实例(但通常不会)。

6. 使用 functools.lru_cache

Python 标准库中的 lru_cache 装饰器可以缓存函数的返回值,利用这一特性也可以实现单例。

示例:

from functools import lru_cache

class Singleton:
    def __init__(self, name):
        self.name = name

@lru_cache(maxsize=1)
def get_singleton(name):
    return Singleton(name)

# 使用
a = get_singleton("Alice")
b = get_singleton("Bob")
print(a is b)   # True
print(a.name)   # Alice
print(b.name)   # Alice

优点:

  • 代码简洁,利用标准库。
  • 自动线程安全(lru_cache 内部有锁)。

缺点:

  • 只能用于函数,不能直接作用于类。
  • 返回值类型为函数返回,类型判断可能受影响。

7. 使用 __init__ 限制实例化次数

另一种思路是直接限制 __init__ 只执行一次,但允许 __new__ 每次都返回同一个实例。

示例:

class Singleton:
    _instance = None
    _initialized = False

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

    def __init__(self, name):
        if not self._initialized:
            self.name = name
            self._initialized = True

这种方式是第 4 种方法的改进,通过标志位避免重复初始化。

三、总结与对比

方法 优点缺点  
模块级单例最简单,天然线程安全  无法控制实例化时机,无延迟加载
类装饰器语法简洁,可传参类型检查破坏,多线程需加锁
元类面向对象,控制力强复杂度高,需理解元类
__new__ 重写最直观,Pythonic__init__ 重复执行,需额外处理
类方法完全控制实例获取,避免重复初始化使用不自然,仍可绕过  
lru_cache简洁,线程安全  只能用于函数,不适合直接作用于类
__init__ 限制解决了 __new__ 重复初始化的问题仍需要 __new__配合,代码稍显冗长

选择建议:

1.如果只需要一个简单的单例,且无需延迟加载,模块级单例是最佳选择。
2.如果需要灵活控制且类型检查无关紧要,类装饰器或 lru_cache 都很方便。
3.对于大型项目或需要清晰继承关系的场景,推荐使用元类或 __new__ 重写。
4.如果希望严格遵循传统单例模式(禁止直接实例化),可以考虑类方法方式。

线程安全注意事项:

在多线程环境下,上述部分实现(如简单的 __new__ 或元类)不是线程安全的,需要添加锁机制。模块级单例和 lru_cache 是线程安全的。其他方式可以通过 threading.Lock 进行保护。

四、总结

单例模式在 Python 中有多种实现方式,每种都有其适用场景。开发者需要根据项目的实际需求,权衡代码简洁性、可维护性以及性能要求,选择最合适的方法。

到此这篇关于Python中实现单例的几种方式小结的文章就介绍到这了,更多相关Python 单例内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 为python设置socket代理的方法

    为python设置socket代理的方法

    这篇文章主要介绍了为python设置socket代理的方法,需要的朋友可以参考下
    2015-01-01
  • Python统计词频并绘制图片(附完整代码)

    Python统计词频并绘制图片(附完整代码)

    这篇文章主要介绍了Python统计词频并绘制图片(附完整代码)本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • DataFrame窗口函数rolling()的用法

    DataFrame窗口函数rolling()的用法

    这篇文章主要介绍了DataFrame窗口函数rolling()的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • python 嵌套型partials的使用

    python 嵌套型partials的使用

    这篇文章主要介绍了python 嵌套型partials的使用,partial对象中包含partial对象的使用,下文更多详细介绍需要的小伙伴可以参考一下
    2022-03-03
  • Django使用Celery异步任务队列的使用

    Django使用Celery异步任务队列的使用

    这篇文章主要介绍了Django使用Celery异步任务队列的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • django-xadmin根据当前登录用户动态设置表单字段默认值方式

    django-xadmin根据当前登录用户动态设置表单字段默认值方式

    这篇文章主要介绍了django-xadmin根据当前登录用户动态设置表单字段默认值方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03
  • 深入理解 python 虚拟机

    深入理解 python 虚拟机

    这篇文章主要介绍了深入理解 python 虚拟机的相关资料,需要的朋友可以参考下
    2023-04-04
  • Django命名URL和反向解析URL实现解析

    Django命名URL和反向解析URL实现解析

    这篇文章主要介绍了Django命名URL和反向解析URL实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • 在Python中使用PIL模块处理图像的教程

    在Python中使用PIL模块处理图像的教程

    这篇文章主要介绍了在Python中使用PIL模块处理图像的教程,PIL模块在Python编程中也是十分常用的模块,示例代码基于Python2.x版本,需要的朋友可以参考下
    2015-04-04
  • Python方法的重载和方法的覆盖

    Python方法的重载和方法的覆盖

    本文主要Python方法的重载和方法的覆盖,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-12-12

最新评论