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


    使用场景:

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

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

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

    1. 模块即单利

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

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

    2. __new__方法实现

        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
    
        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. 使用装饰器来获取单例对象

        # 装饰器(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)可以控制类的创建过程,它主要做三件事:

    • 拦截类的创建
    • 修改类的定义
    • 返回修改后的类
        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. 线程安全单利模式

        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. 元编程线程安全的单利模式

    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)-单例模式

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

  • 相关阅读:
    洛谷-P1427 小鱼的数字游戏
    洛谷-P1047 校门外的树
    洛谷-P1046 陶陶摘苹果
    洛谷-P1980 计数问题
    洛谷-P1424 小鱼的航程(改进版)
    洛谷-P1423 小玉在游泳
    洛谷-P1035 级数求和
    洛谷-P1008 三连击
    Oracle 11g r2 rac +openfiler 2.99 安装
    26 主备库并行复制策略
  • 原文地址:https://www.cnblogs.com/panlq/p/12355917.html
Copyright © 2020-2023  润新知