什么是单例模式? 单例模式(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 ,对博客内容进行验证之后发布,希望对大家理解单例模式提供帮助。