Python面向对象之单例模式的四大实现方法详解
更新时间:2026年10月03日 08:27:53 作者:星河耀银海
本文将用通俗易懂的方式为大家详细介绍Python中单例模式的四种实现方法,包括__new__、装饰器、元类和模块并对比优缺点,通过日志管理器和数据库连接池实战案例,帮你彻底掌握单例模式,写出更高效的代码
一、开篇:只能有一个实例的类
单例模式(Singleton)是最常用的设计模式之一:保证一个类只有一个实例,并提供全局访问点。典型的场景包括:数据库连接池、配置管理器、日志器、应用状态管理器。
先看一个直观的需求:
# 问题:全局配置应该只有一个实例 # 如果每个模块都创建新的Config对象,配置就会不一致 # ❌ 多个实例——配置不一致 # config1 = AppConfig() # debug=True # config2 = AppConfig() # debug=False —— 这是另一个配置了! # ✅ 单例——只有一个实例 # config1 = AppConfig() # 创建实例 # config2 = AppConfig() # 返回同一个实例 # config1 is config2 # True —— 同一个对象
二、Python实现单例的四种方式
2.1 使用__new__(最常用)
class Singleton:
"""单例——通过__new__控制实例创建"""
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self, value=None):
# ⚠️ __init__每次都会调用!需要防护
if self._initialized:
return
self.value = value
self._initialized = True
# 测试
s1 = Singleton("第一次")
s2 = Singleton("第二次")
print(s1 is s2) # True —— 同一个实例
print(s1.value) # 第一次 —— __init__只执行了一次
print(s2.value) # 第一次2.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, connection_string="default"):
self.connection_string = connection_string
print(f"创建数据库连接: {connection_string}")
# 测试
db1 = Database("mysql://localhost/mydb")
db2 = Database("postgresql://localhost/testdb")
print(db1 is db2) # True
print(db1.connection_string) # mysql://localhost/mydb
print(db2.connection_string) # mysql://localhost/mydb
# ⚠️ 装饰器方式的缺点:
# Database现在是一个函数而不是类
print(type(Database)) # <class 'function'>
# isinstance检查不适用2.3 使用元类(最彻底的实现)
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 Config(metaclass=SingletonMeta):
"""配置管理器——单例(通过元类)"""
def __init__(self, debug=False):
# 不需要_initialized防护——元类控制__init__只调用一次
self.debug = debug
self.settings = {}
print(f"Config初始化: debug={debug}")
# 测试
c1 = Config(debug=True)
c2 = Config(debug=False)
print(c1 is c2) # True
print(c1.debug) # True —— 第一次初始化的值
print(c2.debug) # True
# 💡 元类方式最优雅——类定义时就决定了单例行为
# __init__自动只执行一次2.4 使用模块(Pythonic方式)
# 💡 Python的模块本身就是单例!
# 模块在第一次导入时执行,之后被缓存到sys.modules
# 这是最"Pythonic"的单例实现方式
# ===== config.py =====
"""
class AppConfig:
def __init__(self):
self.debug = False
self.host = "localhost"
self.port = 8080
def set_debug(self, enabled):
self.debug = enabled
# 在模块级别创建实例——这就是单例!
config = AppConfig()
"""
# ===== main.py =====
# from config import config
#
# # 所有地方导入的都是同一个config对象
# config.set_debug(True)
#
# # 另一个文件
# from config import config
# print(config.debug) # True —— 同一个对象
# 💡 优点:简单、不需要任何特殊语法
# 💡 缺点:导入时即创建(不能延迟)、无法传参构造三、单例的实战应用
3.1 日志管理器
import threading
from datetime import datetime
class Logger(metaclass=SingletonMeta):
"""日志管理器——全局唯一"""
def __init__(self):
self._lock = threading.Lock()
self._handlers = []
self._level = "INFO"
def set_level(self, level):
self._level = level
def add_handler(self, handler):
self._handlers.append(handler)
def log(self, message, level="INFO"):
"""线程安全的日志记录"""
with self._lock:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
log_entry = f"[{timestamp}] [{level}] {message}"
# 输出到所有处理器
for handler in self._handlers:
handler(log_entry)
# 默认输出到控制台
if not self._handlers:
print(log_entry)
# 使用——在任何地方获取同一个Logger
def some_function():
logger = Logger()
logger.log("函数执行中...")
def another_function():
logger = Logger()
logger.log("另一个函数执行中...", level="WARNING")
some_function()
another_function()3.2 数据库连接池
from threading import Lock
class ConnectionPool(metaclass=SingletonMeta):
"""数据库连接池——全局唯一"""
def __init__(self, max_connections=10):
self.max_connections = max_connections
self._connections = []
self._in_use = set()
self._lock = Lock()
print(f"连接池创建: 最大{max_connections}个连接")
def get_connection(self):
"""获取一个连接"""
with self._lock:
if len(self._connections) < self.max_connections:
conn = f"连接-{len(self._connections) + 1}"
self._connections.append(conn)
else:
raise RuntimeError("连接池已满")
self._in_use.add(conn)
return conn
def release_connection(self, conn):
"""释放连接"""
with self._lock:
self._in_use.discard(conn)
def status(self):
return {
"total": len(self._connections),
"in_use": len(self._in_use),
"available": len(self._connections) - len(self._in_use),
}
# 使用
pool = ConnectionPool(max_connections=5)
conn1 = pool.get_connection()
conn2 = pool.get_connection()
print(pool.status()) # {'total': 2, 'in_use': 2, 'available': 0}
pool.release_connection(conn1)
print(pool.status()) # {'total': 2, 'in_use': 1, 'available': 1}四、总结
单例模式保证"只有一个实例"。Python提供了多种实现——从简单的模块到彻底的元类。
四种实现对比:
| 方式 | 优点 | 缺点 |
|---|---|---|
__new__ | 简单直观 | 需要防__init__重复 |
| 装饰器 | 简洁 | 类型变成函数 |
| 元类 | 最彻底 | 稍复杂 |
| 模块 | 最Pythonic | 不能延迟创建 |
日常推荐:模块级别实例(最简单)→ __new__方式(需要传参)→ 元类(框架/库)。
到此这篇关于Python面向对象之单例模式的四大实现方法详解的文章就介绍到这了,更多相关Python单例模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
python神经网络MobileNetV3 large模型的复现详解
这篇文章主要为大家介绍了python神经网络MobileNetV3 large模型的复现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2022-05-05
python3 自动打印出最新版本执行的mysql2redis实例
这篇文章主要介绍了python3 自动打印出最新版本执行的mysql2redis实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-04-04


最新评论