一天,小猿(程序猿)正在安静的写代码(制造bug),突然狂风大作,只见,小猿的电脑屏幕出现一个无底的黑洞,小猿被卷入电脑,跌入洞中……
不知过了多久,小猿醒了过来,缓缓的睁开眼,发现自己躺在一个陌生的地方,而此时他眼前走过的是一个个机器人,这些机器人背后都有代号,有文件、内存、磁盘等等。小猿还没来得及惊慌,就被一群背后写着“进程”的机器人组织开会,只见台上坐着一个大佬,背后写着赫赫的三个大字母:CPU,小猿心想,这是进了计算机内部窝了啊,望着身边一个个面无表情的机器人,瞬间感觉弱小、可怜、无助、又饥饿……
原来,这位CPU大佬想做一个应用, 在整个应用的运行过程中,一直保持并维护着唯一的实例。所以组织大家集结思路,谁能做到,承诺一顿大餐作为奖励。小猿心想,这不就是单例吗,就这?能难倒我?于是小猿本着助人为乐(能吃饱饭)的原则,踊跃发言并给出了实现方案:
1.使用装饰器方式实现
1.1函数装饰器方式
def singleton(cls): # 创建一个字典用来保存被装饰类的实例对象 _instance = {} def _singleton(*args, **kwargs): # 判断这个类有没有创建过对象,没有新创建一个,有则返回之前创建的 if cls not in _instance: _instance[cls] = cls(*args, **kwargs) return _instance[cls] return _singleton @singleton class A(object): def __init__(self, a=0): self.a = a a1 = A(1) a2 = A(2) # id()函数可以获取对象的内存地址,同一内存地址即为同一对象 print(id(a1), id(a2))
1.2类装饰器方式
class Singleton(object): def __init__(self, cls): self._cls = cls self._instance = {} def __call__(self): if self._cls not in self._instance: self._instance[self._cls] = self._cls() return self._instance[self._cls] @Singleton class B(object): def __init__(self): pass b1 = B() b2 = B() print(id(b1), id(b2))
CPU大佬看了摇了摇头,若有所思,说,这样是可以实现,但是装饰器的原理和执行顺序很复杂,我不能接受。
小猿反驳说:装饰器是基于面向切面编程思想来实现的,具有很高的解耦性和灵活性,而且本质其实不难理解,装饰器本质上其实还是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。装饰器的本质是函数,主要用来装饰其他函数,也就是为其他函数增加功能……
”打住打住“,没等小猿说完,CPU就打断了他,“我不想听你这些繁琐的解释,还有其他实现方式吗,没有就退下吧!”
小猿心想:这真是一个难缠的人啊!为了面子(主要还是想吃饭),忍了,给出方案2
2.使用类的方式实现
class Singleton(object): def __init__(self, *args, **kwargs): pass @classmethod def get_instance(cls, *args, **kwargs): # hasattr() 函数用于判断对象是否包含对应的属性,这里是看看这个类有没有_instance属性 if not hasattr(Singleton, '_instance'): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance s1 = Singleton() # 使用这种方式创建实例的时候,并不能保证单例 s2 = Singleton.get_instance() # 只有使用这种方式创建的时候才可以实现单例 s3 = Singleton() s4 = Singleton.get_instance() print(id(s1), id(s2), id(s3), id(s4))
小猿解释道: 这种方式的思路就是,调用类的get_instance方法去创建对象,get_instance方法会判断之前有没有创建过对象,有的话也是会返回之前已经创建的对象,不再新创建,但是这样有一个弊端,就是在使用类创建(s3 = Singleton()这种方式)的时候,就不能保证单例了,也就是说在创建类的时候一定要用类里面规定的get_instance方法创建。
CPU说到:“那你解决一下喽”。
小猿又开始在心里嘀咕:站着说话不腰疼,我要不是看上那顿大餐,我才不理你呢!给你方案3
3.使用__new__函数实现
class Singleton(object): def __init__(self): print("__init__") def __new__(cls, *args, **kwargs): print("__new__") if not hasattr(Singleton, "_instance"): print("创建新实例") Singleton._instance = object.__new__(cls) return Singleton._instance obj1 = Singleton() obj2 = Singleton() print(obj1, obj2)
当python实例化一个对象时,是先执行类的__new__方法,当我们没写__new__方法时,默认调用基类object的__new__方法,然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,去实现单例模式 ,我们通过hasattr(Singleton, "_instance")(其中hasattr()的功能是判断一个对象有没有指定的属性)去判断之前有没有实例化过对象,如果有,就直接返回,没有就新创建一个。
附上控制台输出:可以看出,同样实现了单例。
这时CPU大佬说到:”看输出其实执行了两遍__init__方法,既然是同一个对象,初始化两次,这不合理。“
”没关系,那优化一下!“小猿说。
class Singleton(object): def __init__(self): if not hasattr(Singleton, "_first_init"): print("__init__") Singleton._first_init = True def __new__(cls, *args, **kwargs): print("__new__") if not hasattr(Singleton, "_instance"): print("创建新实例") Singleton._instance = object.__new__(cls) return Singleton._instance
通过控制台输出我们可以看到,__init__方法只执行了一次。
到这时,大佬似乎有些满意。小猿又说道: ”为了保证线程安全,我们还可以在类内部加入锁机制!“
import threading class Singleton(object): _instance_lock = threading.Lock() # 线程锁 def __init__(self): if not hasattr(Singleton, "_first_init"): print("__init__") Singleton._first_init = True def __new__(cls, *args, **kwargs): print("__new__") with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): print("创建新实例") Singleton._instance = object.__new__(cls) return Singleton._instance def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task, args=[i, ]) t.start()
此时,CPU大佬露出了欣慰的笑容,大赞,众机器人也都欢呼着,CPU大佬也兑现了大餐的承诺,让人带着小猿去吃大餐。
小猿看到满桌子的山珍海味,开心的合不拢嘴,大口大口地吃了起来……
叮铃铃,叮铃铃,闹铃响了……原来这一切只是小猿做的梦,而此时小猿的嘴里还咬着一只袜子……哈哈哈
借此,梳理开发中经常用到的单例模式,其实python实现单例的方式还有很多,像通过模块、metaclass方式等等,这里不一一实现,感兴趣的朋友可以自行百度。
最后,感谢女朋友在生活中,工作上的包容、理解与支持 !