本文最后更新于:2020年11月6日 下午
单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在
当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场
在那些需要“创建连接”的内容中使用能节约很多资源
Python实现方式
模块方式
其实,Python
的模块就是天然的单例模式
因为模块在第一次导入时,会生成.pyc
文件,当第二次导入时,就会直接加载.pyc
文件,而不会再次执行模块代码
因此,只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了
例:
single.py | class Singleton(object): def foo(self): pass single = Singleton()
|
直接在其他文件中导入此文件中的对象single
,这个对象即是单例模式的对象
a.py | from demo.my_singleton import single single.foo()
|
装饰器方式
函数装饰器
| def singleton(cls): _instance = {}
def inner(): if cls not in _instance: _instance[cls] = cls() return _instance[cls] return inner @singleton class Cls(object): def __init__(self): pass
cls1, cls2 = Cls(), Cls() print(id(cls1) == id(cls2))
|
- id函数可以查看对象在内存中的位置
- 使用这个方式创建对象时,会访问singleton函数
实际上,由于装饰器的应用,Cls
返回的结果也变成了函数
函数会查看想要创建实例的类是否在字典中已经有记录,要是有记录就直接返回记录中的对象
类装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Singleton(object): _INSTANCE = {} def __init__(self, cls): self.cls = cls def __call__(self, *args, **kwargs): instance = self._INSTANCE.get(self.cls, None) if not instance: instance = self.cls(*args, **kwargs) self._INSTANCE[self.cls] = instance return instance def __getattr__(self, key): return getattr(self.cls, key, None) @Singleton class my_cls(object): pass
cls1, cls2 = Cls2(), Cls2() print(id(cls1) == id(cls2))
|
虽然和函数装饰器看上去并不一样,但实际上原理是一样的
类方式
使用属性(不要在多线程环境使用)
| import time class Singleton(object): __instance = None def __init__(self, x): self.x = x print(x) time.sleep(1) @classmethod def my_singleton(cls, *args, **kwargs): with cls.__instance_lock: if not cls.__instance: cls.__instance = cls(*args, **kwargs) return cls.__instance
|
这种方式如果不加锁,在多线程环境使用的时候会出现失效的情况
过程很容易想象,假设现在有A,B两个线程
- A
if not cls.__instance:
判断为真
- B
if not cls.__instance:
判断为真
- B
cls.__instance = cls(*args, **kwargs)
初始化了一遍实例
- A
cls.__instance = cls(*args, **kwargs)
初始化了两遍实例
这种方式仅在单线程情况下推荐使用
多线程模式中不推荐使用,加锁会明显的降低性能
使用__new__
当我们实例化一个对象时,是先执行了类的__new__
方法实例化对象;然后再执行类的__init__
方法,对这个对象进行初始化
| import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self, x): self.x = x import time time.sleep(1) def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): with cls._instance_lock: cls._instance = super(Singleton, cls).__new__(cls) return cls._instance
|
元类方式
| class Singleton(type): def __init__(cls, name, bases, dic): super(Singleton, cls).__init__(name, bases, dic) cls._instance = None def __call__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super(Singleton, cls).__call__(*args, **kwargs) # cls._instance = cls(*args, **kwargs) # Error! Lead to call this function recursively return cls._instance class my_cls(object): __metaclass__ = Singleton
|
这个例子中我们使用元类Singleton
替代默认使用type
方式创建类my_cls
可以将类my_cls
看做是元类Singleton
的一个对象,当我们使用my_cls(...)
的方式创建类my_cls
的对象时,实际上是在调用元类Singleton
的对象my_cls
对象可以以函数的方式被调用,那么要求类中定义__call__
函数。不过此处被调用的是类,因此我们在元类中定义函数__call__
来控制类my_cls
对象创建的唯一性
这种方式的弊端之一就是类唯一的对象被存放在类的一个静态数据成员中,外部可以通过class_name._instance
的方式修改甚至删除这个实例(该例中my_cls._instance = None
完全合法)