跳到主要内容

单例模式

问题

Python 中如何实现单例模式?有哪些方式?

答案

方式一:模块级变量(最 Pythonic)

Python 模块天然是单例——首次 import 时执行,之后缓存在 sys.modules

config.py
class _Config:
def __init__(self):
self.debug = False
self.db_url = "postgresql://localhost/db"

# 模块级单例
config = _Config()
# 其他模块直接导入
from config import config
config.debug = True

方式二:__new__ 方法

class Singleton:
_instance = None

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

a = Singleton()
b = Singleton()
assert a is b # True

方式三:装饰器

from functools import wraps

def singleton(cls):
instances = {}

@wraps(cls)
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, url: str):
self.url = url

方式四:元类

class SingletonMeta(type):
_instances: dict = {}

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

线程安全单例

import threading

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

def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None: # 双重检查
cls._instance = super().__new__(cls)
return cls._instance

常见面试问题

Q1: 哪种方式最推荐?

答案

模块级变量最推荐。Python 的模块导入机制天然保证只初始化一次,代码最简洁,且是线程安全的(import 加锁)。只有在需要懒初始化或创建参数化单例时才考虑其他方式。

Q2: 单例模式的缺点?

答案

  1. 难以测试:全局状态导致测试间互相影响
  2. 隐藏依赖:代码内部直接引用单例,依赖不显式
  3. 替代方案:使用依赖注入(FastAPI Depends)代替全局单例

Q3: __new____init__ 的区别?

答案

  • __new__:创建实例(分配内存),返回实例对象
  • __init__:初始化实例(设置属性),无返回值

单例用 __new__ 控制是否创建新实例

相关链接