zoukankan      html  css  js  c++  java
  • 一篇文章带你彻底了解python的单例模式

    什么是单例模式?
    单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场
    
    单例模式的应用场景有哪些?
        python 的logger就是一个单例模式,用以日志记录
        Windows 的资源管理器是一个单例模式
        线程池,数据库连接池一般也用单例模式
        网站计数
    从这些应用场景我们可以总结以下情况需要单例模式
        当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,
        它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;当有同步需要的时候,
        可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,
        这种情况下由于只有一个实例,所以不用担心同步问题。
    
    单例模式的实现方法:
    
    1 使用模块:python的模块是天然的单例模式,因为模块在第一次导入的时候,
    会生成 .pyc文件,当第二次导入时,就会直接加载 .pyc文件,而不会再次执行模块代码。因此,
    我们只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
    
    eg:
        class Singleton(object):
        def foo(self):
            print('test')
            ...
    
    
        singleton = Singleton()
    
    将上面代码保存在文件mysingleton.py中 使用时,直接在其其他文件中倒入此文件中的对象,这个对象即是单例模式的对象
    
    eg:
        from mysingleton import singleton
    
    2 使用装饰器
    装饰器的外层变量定义一个字典,里面存放这个类的实例。当第一次创建的时候就将这个实例保存到字典当中
    然后每次创建的时候都去字典中判断一下,如果已经被实例化,就直接取这个实例化对象,如果不存在就保存在字典中
    
    eg:
    def Singleton(cls):
        _instance = {}
    
        def _singleton(*args, **kwargs):
            if cls not in _instance:
                _instance[cls] = cls(*args, **kwargs)
            return _instance[cls]
    
        return _singleton
    
    
    @Singleton
    class A(object):
        a = 1
    
        def __init__(self, x):
            # print(x)
            self.x = x
    
        def get_x(self):
            # print(self.x)
            return self.x
    
    
    a1 = A(2)
    a2 = A(3)
    
    print(id(a1), a1.get_x())
    print(id(a2), a2.get_x())
    
    结果:
    140370543801064 2
    140370543801064 2
    
    不加装饰器:
    140651110265576 2
    140651110267648 3
    
    3 使用类:
    思路就是,调用类的instance方法,这样有一个弊端就是在使用类创建的时候
    并不是单例,也就是说创建类的时候一定要用类里面规定的方法创建
    eg:
    class Singleton(object):
        def __init__(self, *args, **kwargs):
            pass
    
        @classmethod
        def instance(cls, *args, **kwargs):
            # 利用反射,看看这个类有没有_instance 属性
            if not hasattr(Singleton, '_instance'):
                Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance
    
    
    s1 = Singleton() #使用这种方法创建实例的时候,并不能保证是单例
    s2 = Singleton.instance() #只有使用这种方式创建的时候,才可以实现单例
    s3 = Singleton()
    s4 = Singleton.instance()
    
    print(id(s1),id(s2),id(s3),id(s4),sep='
    ')
    
    结果:
    140721137283816
    140721137285888
    140721161781936
    140721161864080
    
    注意,这样的单例模式在单线程下是安全的,但是如果遇到多线程,就会出现问题
    如遇到多个线程同时创建这个类的实例的时候就会出现问题
    
    import threading
    import time
    
    class Singleton(object):
        def __init__(self, *args, **kwargs):
            time.sleep(1)
            pass
    
        @classmethod
        def instance(cls, *args, **kwargs):
            # 利用反射,看看这个类有没有_instance 属性
            if not hasattr(Singleton, '_instance'):
                Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance
    
    
    def task(arg):
        obj = Singleton.instance(arg)
        print(obj)
    
    for i in range(10):
        t=threading.Thread(target=task,args=[i,])
    
        t.start()
    结果1:
    <__main__.Singleton object at 0x7f8f9ebd8358>
    <__main__.Singleton object at 0x7f8f9d76f2e8>
    <__main__.Singleton object at 0x7f8f9ee52160>
    <__main__.Singleton object at 0x7f8f9ebd8358>
    <__main__.Singleton object at 0x7f8fa22964a8>
    <__main__.Singleton object at 0x7f8f9ee55cf8>
    <__main__.Singleton object at 0x7f8f9d76f2e8>
    <__main__.Singleton object at 0x7f8f9ee61e80>
    <__main__.Singleton object at 0x7f8f9ee55cf8>
    <__main__.Singleton object at 0x7f8fa2296978>
    
    
    结果2
    <__main__.Singleton object at 0x7fc9dc75f4a8>
    <__main__.Singleton object at 0x7fc9daad8358>
    <__main__.Singleton object at 0x7fc9dc75fa20>
    <__main__.Singleton object at 0x7fc9dc75a8d0>
    <__main__.Singleton object at 0x7fc9dc75afd0>
    <__main__.Singleton object at 0x7fc9dc75aeb8>
    <__main__.Singleton object at 0x7fc9dc75abe0>
    <__main__.Singleton object at 0x7fc9dc75aac8>
    <__main__.Singleton object at 0x7fc9dc75f208>
    <__main__.Singleton object at 0x7fc9dc75f0b8>
    结果1 是不加休眠时间的多线程执行结果,结果不是特别明显,但是在init
    方法中有阻塞,就可以看到非常明显的结果,结果2创建了10个不同的实例对象,这是因为一个对象
    创建过程中,另外一个对象也创建了,当他进行判断的时候,会先获取_instance 属性
    ,因为这个时候_instance 属性还不存在,他就会调用init方法,结果就是调用了10次,创建了10
    个对象。
    如何解决这个问题:
    加锁
    在哪里加锁呢?在获取对象属性_instance的时候加锁,如果已经有人在获取对象了,其他人如果要获取这个对象,就要等一下
    因为前面那个可能是第一次创建对象。
    import threading
    import time
    
    class Singleton(object):
        _instance_lock=threading.Lock()
        def __init__(self, *args, **kwargs):
            time.sleep(1)
            pass
    
        @classmethod
        def instance(cls, *args, **kwargs):
            # 利用反射,看看这个类有没有_instance 属性
            with Singleton._instance_lock:
                if not hasattr(Singleton, '_instance'):
                    Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance
    
    
    def task(arg):
        obj = Singleton.instance(arg)
        print(obj)
    
    for i in range(10):
        t=threading.Thread(target=task,args=[i,])
    
        t.start()
    time.sleep(20)
    obj = Singleton.instance()
    print(obj)
    加锁之后的结果:
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    <__main__.Singleton object at 0x7f8f73dd8358>
    这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,
    下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,
    把intance方法,改成下面的这样就行:
    
    import threading
    import time
    
    class Singleton(object):
        _instance_lock=threading.Lock()
        def __init__(self, *args, **kwargs):
            time.sleep(1)
            pass
    
        @classmethod
        def instance(cls, *args, **kwargs):
            # 利用反射,看看这个类有没有_instance 属性
            if not hasattr(Singleton, "_instance"):
                with Singleton._instance_lock:
                    if not hasattr(Singleton, '_instance'):
                        Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance
    
    
    def task(arg):
        obj = Singleton.instance(arg)
        print(obj)
    #
    for i in range(10):
        t=threading.Thread(target=task,args=[i,])
    
        t.start()
    
    time.sleep(20)
    obj = Singleton.instance()
    print(obj)
    
    4 基于__new__方法实现(推荐使用,方便)
    通过上面的例子,我们可以知道实现单例时,为了保证线程安全需要在内部加入锁,
    我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__)
    实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所以我们可以基于这个实现单例模式
    
    import threading
    
    import time
    
    class Singleton(object):
        _instance_lock=threading.Lock()
    
        def __init__(self):
            pass
    
        def __new__(cls, *args, **kwargs):
            if not hasattr(Singleton,'_instance'):
                with Singleton._instance_lock:
                    if not hasattr(Singleton,'_instance'):
                        Singleton._instance=object.__new__(cls)
            return Singleton._instance
    
    obj1=Singleton()
    obj2=Singleton()
    
    print(obj1,obj2,sep='
    ')
    
    def task(arg):
        obj=Singleton()
    
        print(obj)
    
    for i in range(10):
        t=threading.Thread(target=task,args=[i,])
        t.start()
    
    结果:
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    <__main__.Singleton object at 0x7fdab0ec4278>
    采用这种方法的单例模式,以后实例化对象,和平时实例化对象方法一样,
    obj=Singleton()
    
    5 基于 metaclass 方法实现
    
    类由type创建,创建类时,type的__init__ 方法自动执行,类()执行type的
    __call__方法(类的__new__方法,类的__init__方法)
    对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法
    例子:
    
    class Foo(object):
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            pass
    
    
    obj = Foo()
    # 执行type的__call__方法,调用Foo类(是type的对象)的__new__方法,用于创建
    # 对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
    obj()  # 执行Foo的 __call__ 方法
    
    
    # 元类的使用
    
    class SingletonType(type):
        def __init__(self, *args, **kwargs):
            super(SingletonType, self).__init__(*args, **kwargs)
    
        def __call__(cls, *args, **kwargs):
            obj = cls.__new__(cls, *args, **kwargs) #这里的cls,即Foo类
    
            cls.__init__(obj, *args, **kwargs)
            return obj
    
    
    class Foo(metaclass=SingletonType): #指定创建Foo的type为SingletonType
    
        def __init__(self,name):
            self.name=name
    
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    obj=Foo('xx')
    
    基于这种方法实现的单例:
    import threading
    
    
    class SingletonType(type):
        _instance_lock = threading.Lock()
    
        def __call__(cls, *args, **kwargs):
            if not hasattr(cls, '_instance'):
                with SingletonType._instance_lock:
                    if not hasattr(cls, '_instance'):
                        cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
            return cls._instance
    
    
    class Foo(metaclass=SingletonType):
    
        def __init__(self,name):
            self.name = name
    
    
    obj1=Foo('name')
    
    obj2=Foo('hello')
    
    print(obj1,obj2,sep='
    ')
    
    结果:
    <__main__.Foo object at 0x7fe5675d8390>
    <__main__.Foo object at 0x7fe5675d8390>

    本博客借鉴博客地址:https://www.cnblogs.com/huchong/p/8244279.html ,对博客内容进行验证之后发布,希望对大家理解单例模式提供帮助。

  • 相关阅读:
    Confluence 6 LDAP 服务器配置
    在 Confluence 6 中连接一个 LDAP 目录
    Confluence 6 设置公共访问
    Confluence 6 为站点禁用匿名用户访问
    Confluence 6 为站点启用匿名用户访问
    Confluence 6 配置用户目录
    Confluence 6 设置公共访问备注
    Confluence 6 为空间赋予公共访问
    Confluence 6 更新目录
    Omad群组部署、依赖部署一键解决
  • 原文地址:https://www.cnblogs.com/liangliangzz/p/14302274.html
Copyright © 2011-2022 走看看