单例模式
单例模式需要注意的地方在于
- 如何保证创建的实例是唯一的
- 如何保证多线程情况下的访问
- 清晰明了
目前有这么几种方法:
- module
- metaclass
- __new__
- decorator
module
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:
首先在一个py文件中定义这么一个单例类:
class Singleton(object): def __init__(self): self._val = 10 @property def val(self): return self._val @val.setter def val(self, val): self._val = valsingleton = Singleton()
然后再在其它的文件中import
from Singleton import singletonassert singleton.val == 10singleton.val = 20assert singleton.val == 20
但是这个有个缺点,就是程序一开始的时候就进行了创建,造成不必要的浪费,最好是在使用的时候才进行创建,还有另外一个缺点在于,如果有人不遵守规定使用已经创建好的实例,而是自己创建则无法实现单例。
metaclass
这正是元类可以做的事情-通过声明一个元类,我们告诉Python把类对象的创建路由到我们提供的另一个类,从而达到拦截类的创建的目的。用户唯一需要做的是,它们需要声明元类,每个这么做的都会自动获取到元类所提供的扩展。
class Singleton(type): def __init__(cls, what, bases, dict): super().__init__(what, bases, dict) cls._instance = None def __call__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__call__(*args, **kwargs) return cls._instanceclass MyClass(metaclass=Singleton): passclass1 = MyClass()class2 = MyClass()assert class1 == class2
__new__
如果在子类中如果没有重载__new__
方法,那么子类则会顺着继承关系,找到父类中的__new__
,但是如果子类已经重载了,而且没有显式的调用父类的方法,那么就相当于再次进行重载,就不会再去调用父类的__new__
方法,需要注意!
class Singleton(object): _instance = None def __new__(cls, *args, **kw): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) return cls._instanceclass MyClass(Singleton): passclass1 = MyClass()class2 = MyClass()assert id(class1) == id(class2)
装饰器
在装饰器中,我们通过拦截它的__new__
实现,判断它是否存在于该类的__dict__
字典中,如果存在则返回,不存在则创建该类并且存放于__dict__
中,并且进行相应的初始化。
def singleton(cls, *args, **kw): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls(*args, **kw) return instances[cls] return getinstance@singletonclass myclass: passclass1 = myclass()class2 = myclass()assert class1 == class2