浅谈单例模式及其应用场景(Python)

使用场景:

  • Python的logger就是一个单例模式,用以日志记录
    • Windows的资源管理器是一个单例模式
      • 线程池,数据库连接池等资源池一般也用单例模式
      • 网站计数器

从这些使用场景我们可以总结下什么情况下需要单例模式:

Copy
1. 当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次; 当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。

1. 模块即单利#

常见的场景即logging模块,写好一个日志类模块后,并实例化一个logger, 整个项目就调用这个logger

Copy
// logging_module.py class LoggerMgr(object): def foo(self): pass mylogger = LoggerMgr()

2. __new__方法实现#

Copy
class Singleton(object): __instance = None def __new__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs) # 可以在这里给实力对象绑定一些固有属性 # cls.__instance.appkey = "" return cls.__instance
Copy
class Singleton(object): def __new__(cls, *args, **kwargs): # 判断是否存在类属性_instance,_instance是类CCP的唯一对象,即单例 if not hasattr(Singleton, "__instance"): cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs) # cls.__instance = object.__new__(cls) return cls.__instance

但是以上的方法在多线程中会有线程安全问题,当有多个线程同时去初始化对象时,就很可能同时判断__instance is None,从而进入初始化instance的代码中(如果有__init__方法)。所以需要用互斥锁来解决这个问题。

3. 使用装饰器来获取单例对象#

Copy
# 装饰器(decorator)可以动态地修改一个类或函数的功能 import functools def singleton(cls): __instance = {} @functools.wraps(cls) def getinstance(*args, **kwargs): if cls not in __instance: __instance[cls] = cls(*args, **kwargs) return __instance[cls] return getinstance @singleton class MyClass(object): a = 1

我们定义了一个装饰器 singleton,它返回了一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls 作为 key,cls(*args, **kw) 作为 value 存到 instances 中,否则,直接返回 instances[cls]。

4. 使用metaclass元类创建单例#

元类(metaclass)可以控制类的创建过程,它主要做三件事:

  • 拦截类的创建
  • 修改类的定义
  • 返回修改后的类
Copy
class Singleton(type): __instances = {} def __call__(cls, *args, **kwargs): if cls not in cls.__instances: cls.__instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls.__instances[cls] # python2写法 # class MyClass(object): # __metaclass__ = Singleton() # python3写法 class MyClass(metaclass=Singleton): def __init__(self): self.blog = "blog"

5. 线程安全单利模式#

Copy
import threading def make_synchronized(func): import threading func.__lock__ = threading.Lock() # 用装饰器实现同步锁 def synced_func(*args, **kwargs): with func.__lock__: return func(*args, **kwargs) return synced_func class Singleton(object): __instance = None @make_synchronized def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def __init__(self): self.blog = "blog" # ------------- def worker(): e = Singleton() print(id(e)) def meta(): e1 = Singleton() e2 = Singleton() e1.blog = 123 print(e1.blog) print(e2.blog) print(id(e1)) print(id(e2)) if __name__ == "__main__": meta() tasks = [threading.Thread(target=worker) for _ in range(20)] for task in tasks: task.start() task.join()

6. 元编程线程安全的单利模式#

Copy
import threading class MetaSingleton(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): with MetaSingleton._instance_lock: if not hasattr(cls, '_instance'): cls._instance = super(MetaSingleton, cls).__call__(*args, **kwargs) return cls._instance class Singleton(metaclass=MetaSingleton): def __init__(self, name): self.name = name

7. 参考文献#

Python 中的单例模式
设计模式(Python)-单例模式

高并发下线程安全的单例模式

posted @   JonPan  阅读(4348)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示
CONTENTS