单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
- 正常情况下 类名只要加括号实例化产生对象 执行几次就会产生几个不同的对象
- 例如,一个系统可以存在多个打印任务,但是只能有一个正在工作的任务,一个音乐播放器里可以播放很多音乐,但是一次只能播放一个音乐
- 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源
class Person:
pass
p1 = Person()
p2 = Person()
print(p1)
print(p2)
单例模式,常见的实现方式有5种
使用模块
- Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
使用元类干预对象的创建过程
- 使用 metaclass:完美的单例实践,也推荐使用,但存在多线程的安全隐患,应该在元类的 —__call__ 部分加锁
class MyMetaClass(type):
instance = None # 记录类是否已经创建了对象
def __call__(self, *args, **kwargs):
if self.instance:
return self.instance
# 获取对象
obj = super().__call__(*args, **kwargs)
# 保存对象
self.instance =obj
# 返回对象
return obj
class Single(metaclass=MyMetaClass):
def __init__(self, name):
self.name =name
obj1 = Single('tom')
obj2 = Single('kevin')
obj3 = Single('tony')
print(obj1.name,id(obj1)) # tom 18054064
print(obj2.name,id(obj2)) # tom 18054064
print(obj3.name,id(obj3)) # tom 18054064
定义一个装饰器实现单例模式
- 使用装饰器:不能被继承,不推荐使用,被装饰的类将无法被继承(因为被装饰的类变成了函数)
def singleton(cls):
def inner(*args,**kwargs):
if hasattr(cls,'_instance'):
return getattr(cls,'_instance')
obj=cls(*args,**kwargs)
setattr(cls,'_instance',obj)
return obj
return inner
@singleton
class Person:
pass
p1 = Person()
p2 = Person()
print(p1)
print(p2)
print(p1 is p2)
基于new方法实现单例模式
- 使用 new 方法:需要开发成员有共同的技术背景,对Python有足够的认识,不然代码会有BUG。单纯使用普通类的 new 方法的时候,也是会涉及到继承问题,就是使用 _instance 会被子类继承,除非每个子类手动声明 _instance = None。而使用 __instance 的话,则会因为 python 的机制,导致使用 hasattr 判定的话,__instance 会变形为 _类名__instance。
class Singleton(object):
_instance=None
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls)
return cls._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)
使用类方法
- 使用classmethod:不推荐使用
class Singleton(object):
_instance=None
def __init__(self):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not cls._instance:
cls._instance=cls(*args, **kwargs)
return cls._instance
a1=Singleton.instance()
a2=Singleton().instance()
print(a1 is a2)