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

相关推荐
爱笑的眼睛113 小时前
深度解析现代OCR系统:从算法原理到高可用工程实践
java·人工智能·python·ai
BoBoZz193 小时前
PerlinNoise Perlin噪声(PerlinNoise)隐式函数构建模型并渲染
python·vtk·图形渲染·图形处理
kkoral3 小时前
基于MS-Swift 为 Qwen3-0.6B-Base 模型搭建可直接调用的 API 服务
python·conda·fastapi·swift
PythonFun3 小时前
WPS多维表结合Python实现在线批量查词
开发语言·python·wps
爱笑的眼睛114 小时前
深入 Django 表单 API:从数据流到高级定制
java·人工智能·python·ai
万粉变现经纪人4 小时前
Python系列Bug修复PyCharm控制台pip install报错:如何解决 pip install 网络报错 企业网关拦截 User-Agent 问题
网络·python·pycharm·beautifulsoup·bug·pandas·pip
AIOps打工人4 小时前
Grafana Query MCP:基于FastAPI的Grafana查询转换与分页服务
运维·数据库·python·ai·grafana·fastapi·devops
_OP_CHEN4 小时前
【Python基础】(四)Python 语法基础终篇——函数 / 列表 / 字典 / 文件操作一次吃透!
开发语言·python
小鸡吃米…4 小时前
Python - 数据库访问
数据库·python