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 的实现。

相关推荐
2301_797312261 小时前
学习java37天
开发语言·python
WJSKad12351 小时前
果园树干识别与定位_faster-rcnn_x101-32x4d_fpn_1x_coco改进实践
python
深蓝电商API1 小时前
Scrapy中间件实战:自定义请求头和代理池实现
python·scrapy·中间件
hui函数2 小时前
Python系列Bug修复|如何解决 pip install 安装报错 invalid command ‘bdist_wheel’(缺少 wheel)问题
python·bug·pip
hui函数2 小时前
Python系列Bug修复|如何解决 pip install -r requirements.txt 私有索引未设为 trusted-host 导致拒绝 问题
python·bug·pip
子午2 小时前
【2026原创】动物识别系统~Python+深度学习+人工智能+模型训练+图像识别
人工智能·python·深度学习
o_insist2 小时前
LangChain1.0 实现 PDF 文档向量检索全流程
人工智能·python·langchain
脑洞AI食验员2 小时前
智能体来了:用异常与文件处理守住代码底线
人工智能·python
曲幽2 小时前
FastAPI登录验证:用OAuth2与JWT构筑你的API安全防线
python·fastapi·web·jwt·token·oauth2
幻云20102 小时前
Next.js指南:从入门到精通
开发语言·javascript·人工智能·python·架构