单例模式
问题
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: 单例模式的缺点?
答案:
- 难以测试:全局状态导致测试间互相影响
- 隐藏依赖:代码内部直接引用单例,依赖不显式
- 替代方案:使用依赖注入(FastAPI
Depends)代替全局单例
Q3: __new__ 和 __init__ 的区别?
答案:
__new__:创建实例(分配内存),返回实例对象__init__:初始化实例(设置属性),无返回值
单例用 __new__ 控制是否创建新实例。