Python单例模式实现方法

在Python中,单例模式有多种实现方式,下面介绍几种常见的形式:

  1. 使用模块:Python的模块就是天然的单例模式,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

  2. 使用装饰器:使用装饰器来装饰一个类,使其只能生成一个实例。

  3. 使用类方法(new:通过重写类的__new__方法来实现单例。

  4. 使用元类(metaclass):通过元类来控制类的创建过程,确保只生成一个实例。

  5. 使用共享属性:通过将实例的__dict__属性指向同一个字典,从而使得多个实例共享相同的状态(但这并不是严格意义上的单例,因为多个实例的id可能不同,但状态共享)。

下面分别给出这些方式的代码示例:

1. 使用模块

创建一个模块,比如叫做singleton_module.py,里面定义需要的变量和函数。然后其他地方导入这个模块,这个模块就是单例的。

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:
    pass

3. 使用__new__方法

复制代码
class Singleton:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

# 或者使用更简洁的方式,利用基类
class SingletonBase:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

class MyClass(SingletonBase):
    pass

4. 使用元类

复制代码
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):
    pass

5. 使用共享属性(Borg模式)

复制代码
class Borg:
    _shared_state = {}
    def __init__(self):
        self.__dict__ = self._shared_state

class MyClass(Borg):
    pass

6. 使用全局变量(不推荐,但也是一种方式)

在一个模块中定义一个全局变量,然后通过函数来获取这个变量,确保只有一个实例。

7. 使用线程安全的单例模式(适用于多线程环境)

复制代码
import threading

class Singleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

# 或者使用模块级的锁

注意:在多线程环境下,需要确保单例的创建过程是线程安全的。上面的例子中,我们使用了双重检查锁定模式(Double-Checked Locking Pattern)。

8. 使用lru_cache(Python 3.2+)

复制代码
from functools import lru_cache

@lru_cache(maxsize=1)
class MyClass:
    pass

但请注意,lru_cache通常用于函数,用于类时,它缓存的是类的实例,但类本身可能被多次调用,但返回的是同一个实例。然而,这种方式并不常见,而且需要注意类的参数问题。

实际上,单例模式在Python中并不像在静态语言中那样必要,因为我们可以通过模块来共享状态。但是,如果需要,以上几种方式都可以实现单例。

选择哪种方式取决于具体需求,比如是否要求线程安全,是否希望延迟初始化等。通常情况下,使用模块或者装饰器的方式比较简单,而使用元类的方式则更加灵活和强大。

以下是 Python 中单例模式的几种常见实现方式:

1. 使用模块实现(推荐)

Python 模块本身就是天然的单例模式

复制代码
# singleton_module.py
class Singleton:
    def __init__(self):
        self.value = None
    
    def do_something(self):
        return f"Doing something with {self.value}"

singleton_instance = Singleton()

# 在其他文件中使用
from singleton_module import singleton_instance

singleton_instance.value = "Hello"

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 Database:
    def __init__(self):
        print("Database initialized")
        self.connection = None
    
    def connect(self):
        self.connection = "Connected"

# 使用
db1 = Database()  # 输出: Database initialized
db2 = Database()
print(db1 is db2)  # True

3. 使用 __new__ 方法实现

复制代码
class Singleton:
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, name=None):
        if not hasattr(self, 'initialized') or name:
            self.name = name
            self.initialized = True

# 使用
s1 = Singleton("First")
s2 = Singleton("Second")
print(s1 is s2)        # True
print(s1.name)         # "First" (不会变成"Second")

4. 使用元类实现

复制代码
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 Logger(metaclass=SingletonMeta):
    def __init__(self, log_file="app.log"):
        self.log_file = log_file
        self.logs = []
    
    def log(self, message):
        self.logs.append(message)
        print(f"[LOG] {message}")

# 使用
logger1 = Logger("app.log")
logger2 = Logger("another.log")  # 参数被忽略
print(logger1 is logger2)  # True
print(logger1.log_file)    # "app.log"

5. Borg 模式(共享状态单例)

所有实例共享相同状态,但不是严格意义上的单例

复制代码
class Borg:
    _shared_state = {}
    
    def __init__(self):
        self.__dict__ = self._shared_state
        self.state = "Init"
    
    def __str__(self):
        return self.state

class YourClass(Borg):
    pass

# 使用
obj1 = YourClass()
obj2 = YourClass()
obj1.state = "Running"
print(obj2.state)  # "Running"
print(obj1 is obj2)  # False (不同对象但状态共享)

6. 线程安全的单例

复制代码
import threading

class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, config=None):
        if not hasattr(self, 'initialized'):
            self.config = config or {}
            self.initialized = True

7. 使用 functools.lru_cache (Python 3.8+)

复制代码
from functools import lru_cache

class SingletonClass:
    def __init__(self, value):
        self.value = value

@lru_cache(maxsize=1)
def get_singleton(value):
    return SingletonClass(value)

# 使用
obj1 = get_singleton("test")
obj2 = get_singleton("test")  # 返回缓存的实例
print(obj1 is obj2)  # True

8. 使用类方法实现

复制代码
class ConfigManager:
    _instance = None
    
    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, 'initialized'):
            self.settings = {}
            self.initialized = True

# 使用
config1 = ConfigManager.get_instance()
config2 = ConfigManager.get_instance()
print(config1 is config2)  # True

选择建议

  1. 简单场景:使用模块方式最简单直接

  2. 需要继承:使用元类或装饰器

  3. 多线程环境:使用线程安全版本

  4. 需要控制初始化 :使用 __new__ + __init__ 配合

  5. 共享状态而非单实例:使用 Borg 模式

最佳实践示例(综合版)

复制代码
import threading
from typing import Any

class Singleton(type):
    """线程安全的单例元类"""
    _instances = {}
    _lock = threading.Lock()
    
    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        with cls._lock:
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

class DatabaseConnection(metaclass=Singleton):
    def __init__(self, connection_string: str = ""):
        if not hasattr(self, '_initialized'):
            self.connection_string = connection_string
            self._initialized = True
            print(f"Database connection established: {connection_string}")
    
    def query(self, sql: str):
        print(f"Executing: {sql}")

# 使用
db1 = DatabaseConnection("mysql://localhost:3306")
db2 = DatabaseConnection("different_connection")  # 第二个参数被忽略
print(db1 is db2)  # True

选择哪种方式取决于具体需求,模块方式在大多数情况下是最简单、最 Pythonic 的实现。

相关推荐
Dxy123931021640 分钟前
Python基于BERT的上下文纠错详解
开发语言·python·bert
SiYuanFeng2 小时前
Colab复现 NanoChat:从 Tokenizer(CPU)、Base Train(CPU) 到 SFT(GPU) 的完整踩坑实录
python·colab
炸炸鱼.3 小时前
Python 操作 MySQL 数据库
android·数据库·python·adb
_深海凉_3 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
AC赳赳老秦4 小时前
OpenClaw email技能:批量发送邮件、自动回复,高效处理工作邮件
运维·人工智能·python·django·自动化·deepseek·openclaw
zhaoshuzhaoshu4 小时前
Python 语法之数据结构详细解析
python
AI问答工程师4 小时前
Meta Muse Spark 的"思维压缩"到底是什么?我用 Python 复现了核心思路(附代码)
人工智能·python
zfan5205 小时前
python对Excel数据处理(1)
python·excel·pandas
小饕5 小时前
我从零搭建 RAG 学到的 10 件事
python
老歌老听老掉牙6 小时前
PyQt5+Qt Designer实战:可视化设计智能参数配置界面,告别手动布局时代!
python·qt