python中实现单例模式的几种方法
单例模式:一个软件的设计模式,为了保证一个类,无论调用多少次产生的实例对象,都是指向同一个内存地址,仅仅只有一个实例(只有一个对象)。主要有几种方法来实现:
一、通过导入的方式。
1.基本原理:当我们导入一个py文件,会执行这个模块的代码,然后将这个模块的名称空间加载到内存。当再次导入时,不会再执行该文件,而是直接在内存中找。因此,如果第一次导入模块,执行文件源代码时实例化了一个类,那再次导入的时候,就不会再实例化。
2.代码实现:
# cls_singleton.py class Foo(object): pass instance = Foo() # test.py import cls_singleton obj1 = cls_singleton.instance obj2 = cls_singleton.instance print(obj1 is obj2) # True
二、通过类中的__new__实现。
1.代码实现:
class Singleton:
__instance = None
def __init__(self,num):
self.num =num
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super().__new__(cls) # 调用父类的__new__方法
return cls.__instance
a = Singleton(10)
b = Singleton(10)
print(id(a)) # 1873004801808
print(id(b)) # 1873004801808
# 上述单例模式,实例化后只有一个实例化对象。内存地址一致。
三、通过装饰器实现。
def decorator(cls): instance = {} def func(*args,**kwargs): if cls in instance: return instance[cls] else: # 如果类不在字典中,通过cls创建类的实例对象并添加到这个instance字典中(cls是指向这个类的) instance[cls] = cls(*args,**kwargs) return instance[cls] return func @decorator class A: def __init__(self,name): self.name = name a = A('shao') b = A('bruce') print(id(a)) print(id(b))
针对通过__new__这种方法其实看似已经很完美了,如果同时有多个线程同一时刻(极端条件下)事例化对象,那么就会出现多个对象,这就不再是单例模式了。那么解决这个问题,可以通过加锁来解决。
这样一来只有第一个抢到锁的线程实例化一个对象并保存在_instance中,同一时刻抢锁的其他线程再抢到锁后,不会进入这个判断if not cls._instance,直接把保存在_instance的对象返回了。这样就实现了多线程下的单例模式。
此时还有一个问题需要解决,后面所有再事例对象时都需要再次抢锁,这会大大降低执行效率。解决这个问题也很简单,直接在抢锁前,判断下是否有单例对象了,如果有就不再往下抢锁了。
四、__new__圆满版:
import threading class Singleton: __instance = None __lock = threading.RLock() # 加锁 def __init__(self): pass def __new__(cls, *args, **kwargs): if cls.__instance: # 如果已经有了单例就不再去抢锁,避免IO等待 return cls.__instance with cls.__lock: if not cls.__instance: print('create') cls.__instance = super().__new__(cls) # 调用父类的__new__方法 return cls.__instance a = Singleton() b = Singleton()
五、基于元类type:
1 class Mytype(type): 2 3 def __init__(self,name,bases,attrs): 4 super(Mytype, self).__init__(name,bases,attrs) 5 self.instance = None 6 7 def __call__(self, *args, **kwargs): 8 # 1.判断下是否已有对象 9 if not self.instance: 10 self.instance = self.__new__(self) 11 self.__init__(self.instance, *args, **kwargs) 12 return self.instance 13 14 class Singleton(object,metaclass=Mytype): 15 pass 16 17 class Foo(Singleton): 18 pass 19 20 a = Foo() 21 b = Foo() 22 print(id(a)) 23 print(id(b))