单例模式

单例模式(Singleton Pattern)是一种常见的软件设计模式,主要目的是为了确保某一个类只有一个示例的存在。

例:某个服务器程序的配置信息存放在一个文件夹中,客户端通过一个AppConfig的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建AppConfig对象的实例,这样会导致系统中存在多个AppConfig的实例对象,严重浪费内存

实现单例模式的集中方法

1、使用模块

其实,Python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件。 我们只需要在一个模块中定义函数和数据,就可以获得一个单例对象

  1. class SingLeton:
  2. def foo(self):
  3. pass
  4. singleton = SingLeton()

保存代码到文件mysingleton.py中。

  1. from mysingleton import singleton

2、使用装饰器

  1. def sing_leton(cls):
  2. _instance = {}
  3. def singleton(*args,**kwargs):
  4. if cls not in _instance:
  5. _instance[cls] = cls(*args,**kwargs)
  6. return singleton
  7. @sing_leton
  8. class A:
  9. a = 1
  10. def __init__(self,x=0):
  11. self.x = x
  12. a1 = A(1)
  13. a2 = A(2)

3、使用类

  1. class sing_leton:
  2. def __init__(self):
  3. pass
  4. @classmethod
  5. def get_instance(cls,*args,**kwargs):
  6. if not hasattr(sing_leton,"_instance"):
  7. sing_leton._instance = sing_leton(*args,**kwargs)
  8. return sing_leton._instance

当然一般情况下,大家以为这样就可以完成单例模式了,但是这样在使用多线程的时候会产生线程不安全

  1. import threading
  2. def task(arg):
  3. obj = sing_leton.get_instance()
  4. print(obj)
  5. for i in range(10):
  6. t = threading.Thread(target=task,args=[i,])
  7. t.start()

程序执行↓

  1. <__main__.sing_leton object at 0x000001F80176FAC8>
  2. <__main__.sing_leton object at 0x000001F80176FAC8>
  3. <__main__.sing_leton object at 0x000001F80176FAC8>
  4. <__main__.sing_leton object at 0x000001F80176FAC8>
  5. <__main__.sing_leton object at 0x000001F80176FAC8>
  6. <__main__.sing_leton object at 0x000001F80176FAC8>
  7. <__main__.sing_leton object at 0x000001F80176FAC8>
  8. <__main__.sing_leton object at 0x000001F80176FAC8>
  9. <__main__.sing_leton object at 0x000001F80176FAC8>
  10. <__main__.sing_leton object at 0x000001F80176FAC8>

看起来好像也没有什么毛病,但是真的没有毛病吗?,我们来修改一下相关的代码

  1. class sing_leton:
  2. def __init__(self):
  3. import time
  4. time.sleep(1)
  5. @classmethod
  6. def get_instance(cls,*args,**kwargs):
  7. if not hasattr(sing_leton,"_instance"):
  8. sing_leton._instance = sing_leton(*args,**kwargs)
  9. return sing_leton._instance

执行得到了这样的结果。

  1. <__main__.sing_leton object at 0x000001ED128C4438><__main__.sing_leton object at 0x000001ED128C4F60>
  2. <__main__.sing_leton object at 0x000001ED128C4898><__main__.sing_leton object at 0x000001ED128C45C0>
  3. <__main__.sing_leton object at 0x000001ED128C4F28><__main__.sing_leton object at 0x000001ED128C46D8>
  4. <__main__.sing_leton object at 0x000001ED128C4588><__main__.sing_leton object at 0x000001ED1290EC50>
  5. <__main__.sing_leton object at 0x000001ED128C4550><__main__.sing_leton object at 0x000001ED1290EA58>

那么问题出现了,创建的单例模式,却无法支持多线程的访问。那有什么解决方案呢。

解决方法: 加锁啊!加锁的部分串行,不加锁部分并行。保证了安全。

  1. class sing_leton:
  2. _instance_lock = threading.Lock()
  3. def __init__(self):
  4. import time
  5. time.sleep(2)
  6. @classmethod
  7. def get_instance(cls,*args,**kwargs):
  8. with sing_leton._instance_lock:
  9. if not hasattr(sing_leton,"_instance"):
  10. sing_leton._instance = sing_leton(*args,**kwargs)
  11. return sing_leton._instance

执行如下:

  1. <__main__.sing_leton object at 0x000001ED128A9470>
  2. <__main__.sing_leton object at 0x000001ED128A9470>
  3. <__main__.sing_leton object at 0x000001ED128A9470>
  4. <__main__.sing_leton object at 0x000001ED128A9470>
  5. <__main__.sing_leton object at 0x000001ED128A9470>
  6. <__main__.sing_leton object at 0x000001ED128A9470>
  7. <__main__.sing_leton object at 0x000001ED128A9470>
  8. <__main__.sing_leton object at 0x000001ED128A9470>
  9. <__main__.sing_leton object at 0x000001ED128A9470>
  10. <__main__.sing_leton object at 0x000001ED128A9470>

但是这里还有一个小的问题,就是当我们下一次进行性实例化对象的时候,已经史丹利模式了,但是我们还是加了锁,这样不太好,在进行一些优化。

  1. class sing_leton:
  2. _instance_lock = threading.Lock()
  3. def __init__(self):
  4. import time
  5. time.sleep(2)
  6. @classmethod
  7. def get_instance(cls,*args,**kwargs):
  8. if not hasattr(sing_leton,"_instance"):
  9. with sing_leton._instance_lock:
  10. if not hasattr(sing_leton,"_instance"):
  11. sing_leton._instance = sing_leton(*args,**kwargs)
  12. return sing_leton._instance

这种单例模式会有限制,以后实例化必须通过 instance = sing_leton.get_instance()

4、基于new方法实现(推荐)

当我们实例化一个对象的时,先执行类的__new__方法(没有定义__new__的时候,默认object.__new__),实例化对象;然后执行类的init方法,对这个对象进行初始化。

  1. class sing_leton:
  2. _instance_lock = threading.Lock()
  3. def __init__(self):
  4. pass
  5. def __new__(cls,*args,**kwargs):
  6. if not hasattr(sing_leton,"_instance"):
  7. with sing_leton._instance_lock:
  8. if not hasattr(sing_leton,"_instance"):
  9. sing_leton._instance = sing_leton(*args,**kwargs)
  10. return sing_leton._instance

如果是采用这种方法的话,实例化对象的时候和平时就一样了。instance = sing_leton()

5、基于metaclass方式实现

  1. 1、类由type创建,创建类时,type__init__方法自动执行,类()执行type__call__方法(类的__new__方法,类的__init__方法)
  2. 2、对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法
  1. class sing_leton_Type(type):
  2. _instance_lock = threading.Lock()
  3. def __call__(cls,*args,**kwargs):
  4. if not hasattr(cls,"_instance"):
  5. with sing_leton_Type._instance_lock:
  6. if not hasattr(cls,"_instance"):
  7. cls._instance = super(sing_leton_Type,cls).__call__(*args,**kwargs)
  8. return cls._instance
  9. class Foo(metaclass=sing_leton_Type):
  10. def __init__(self,name):
  11. self.naem = name