一,什么是单例模式
单例模式(Singleton Pattern) 是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
适用场景——该程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。
- 需要一个全局唯一的对象来协调整个系统的行为,如配置管理器,统一管理系统配置。
- 资源共享的情况,如共享的数据库连接池,使用一个数据库对象对数据库进行操作,以维护数据的一致性。
- 控制资源的情况,如管理打印机的使用。
- 日志记录器(Logger):通常在应用程序中只需要一个日志实例,仅使用一个日志类的对象,将多项服务的日志信息按照顺序转储到一个特定的日志文件中。
总之,单例模式的意图就是:
- 确保类有且只有一个对象被创建。
- 为对象提供一个访问点,以使程序可以全局访问该对象。
- 控制共享资源的并行访问。
二,python 代码
(一)最简单的实现
实现单例模式的一个简单方法:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
# 初始化代码可以放在这里
return cls._instance
def some_business_logic(self):
# 单例的业务逻辑方法
pass
# 使用示例
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
- 使用
__new__
方法来控制实例的创建。 - 通过
hasattr(cls, '_instance')
检查类是否已经有一个实例。 - 如果没有实例,就创建一个新的实例并保存在类属性
_instance
中。 - 返回这个唯一的实例。
(二)提高灵活性
改进一下代码:改进版本主要是为了提高代码的可读性和灵活性
# 原始版本
class Singleton(object):
_instance = None
def __new__(cls):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
# 改进版本
class SingletonImproved:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
# 初始化代码可以放在这里
return cls._instance
def __init__(self):
# 初始化代码
if not hasattr(self, 'initialized'):
self.initialized = True
# 其他初始化代码...
def some_business_logic(self):
# 单例的业务逻辑方法
pass
# 使用示例
s1 = SingletonImproved()
s2 = SingletonImproved()
print(s1 is s2) # 输出: True
- 使用类变量
_instance = None
来存储实例,这样更加明确和易读。 - 使用
is None
检查而不是hasattr
,这样更加直观和高效。 - 添加了
__init__
方法来处理初始化逻辑。 - 在
__init__
中使用一个标志来确保初始化代码只运行一次。
(三)带初始化参数
对于需要参数化的单例,可以实现一个带参数的 __new__
方法:
class ParameterizedSingleton:
_instances = {
}
def __new__(cls, *args, **kwargs):
# 将位置参数和关键字参数组合成一个不可变的 key,用作实例的唯一标识
key = (args, frozenset(kwargs.items()))
# 检查是否已经存在对应的实例
if key not in cls._instances:
cls._instances[key] = super().__new__(cls)
return cls._instances[key]
def __init__(self, *args, **kwargs):
# 确保 __init__ 只被调用一次
if not hasattr(self, 'initialized'):
self.args = args
self.kwargs = kwargs
self.initialized = True
print(f"Initializing with args: {
args} and kwargs: {
kwargs}")
def get_parameters(self):
return self.args, self.kwargs
# 创建第一个实例
instance1 = ParameterizedSingleton(10, name="test1")
print(instance1.get_parameters()) # 输出: ((10,), {'name': 'test1'})
# 创建第二个实例,参数不同
instance2 = ParameterizedSingleton(20, name="test2")
print(instance2.get_parameters()) # 输出: ((20,), {'name': 'test2'})
# 创建与第一个实例相同参数的实例
instance3 = ParameterizedSingleton(10, name="test1")
print(instance3.get_parameters()) # 输出: ((10,), {'name': 'test1'})
# 检查单例特性
print(instance1 is instance3) # 输出: True
print(instance1 is instance2) # 输出: False
这个版本:
- 参数化:可以基于不同的参数创建不同的单例实例。
- 灵活性:可以easily扩展到多个参数。
- 内存效率:只为独特的参数组合创建新实例。
(四)线程安全
上面的代码都不是线程安全的:
# 线程不安全的单例实现
class ThreadUnsafeSingleton:
_instance = None
def __new__(cls):
if cls._instance is None:
time.sleep(0.1) # 模拟耗时操作
cls._instance = super().__new__(cls)
return cls._instance
线程不安全的原因:
- 在多线程环境中,多个线程可能同时执行到
if cls._instance is None
这一行。 - 如果这些线程同时发现
_instance
为 None,它们都会尝试创建新实例。 - 这可能导致多个实例被创建,违反了单例模式的核心原则。
线程不安全将导致的问题:
- 多个实例:不同线程可能最终使用不同的实例,导致状态不一致。
- 资源浪费:创建多个不必要的实例可能会浪费资源。
- 不可预测的行为:依赖单例的代码可能会因为使用了不同的实例而表现出意外行为。
这里就用锁来改进:
import threading
import time
# 线程不安全的单例实现
class ThreadUnsafeSingleton:
_instance = None
def __new__(cls):
if cls._instance is None:
time