内容:保证一个类只有一个实例,并提供一个访问它的全局访问点。
使用场景:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
优点:
- 对唯一实例的受控访问。
- 单例相当于全局变量,但防止了命名空间被污染。
实现:
- 使用__new__方法(推荐)
- 当我们实例化一个对象时,是先执行了类的__new__方法(没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所以我们可以基于这个,实现单例模式。
- 思路:在一个类的__new__方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建。
- 实现单例是,为了保证线程安全需要在内部加入锁
-
执行上面代码,可以发现obj1和obj2内存地址一样,表示为同一实例。import threading 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) # <__main__.Singleton object at 0x0000013FF5DD1EF0> <__main__.Singleton object at 0x0000013FF5DD1EF0>
- 装饰器方法
- 思路:装饰器里面的外部变量定义一个字典,里面存放这个类的实例。当第一次创建的时候,就将这个实例保存到这个字典中。以后每次创建对象的时候,都去这个字段中判断一下,如果已经被实例化,就直接取这个实例对象,如果不存在就保存在字典中。
-
执行上面代码,可以发现a1和a2内存地址一样,表示为同一实例。def Singleton(cls): _instance = {} def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] return _singleton @Singleton class A(object): def __init__(self, x=0): self.x = x a1 = A(2) a2 = A(3) print(a1, a2) # <__main__.A object at 0x000001EC70F61E80> <__main__.A object at 0x000001EC70F61E80>
- import方法
- Python的模块是天然的单例模式,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此,我们只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
-
# mysingleton.py class Singleton(object): def foo(self): pass singleton = Singleton()
将上面的代码保存在文件mysingleton.py中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
from a import singleton
- 使用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: # __metaclass__ = Singleton # Python3 class MyClass(metaclass=Singleton): pass one = MyClass() two = MyClass() print(one, two) # <__main__.MyClass object at 0x00000272C06B1198> <__main__.MyClass object at 0x00000272C06B1198>
- 使用类
- 思路:调用类的类方法(classmethod),这样有一个弊端就是在使用类创建的时候,并不是单例。也就是说在创建类的时候一定要用类里面规定的方法创建。比如下面的get_instance方法
-
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self, *args, **kwargs): time.sleep(1) @classmethod def get_instance(cls, *args, **kwargs): if not hasattr(Singleton, '_instance'): with Singleton._instance_lock: if not hasattr(Singleton, '_instance'): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance s1 = Singleton.get_instance() s2 = Singleton.get_instance() print(s1, s2)
这种方法在项目中使用的比较多,但是这种方法不如__new__来的简单。