python 中的单利模式

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

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

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

  1. 实现线程安全的单例模式
    import threading
    try:
        from synchronize import make_synchronized
    except ImportError:
        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()
  1. 使用装饰器来获取单例对象
    # 装饰器(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]。

  1. 使用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"

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

版权声明:本文为panlq原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/panlq/p/12355917.html