Python 实现 Singleton 模式的几种方式,正在准备面试
在 python 中我们经常只需要实现一个装饰器,然后使用该装饰器作用于只能有唯一一个实例的类。这样只需要实现一个这样的装饰器,便可以作用于任何一个想要唯一实例的类。
2.闭包方式
闭包的应用很多,单例模式则是其应用之一。先看代码:
'''
遇到问题没人解答?小编创建了一个 Python 学习交流 QQ 群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和 PDF 电子书!
'''
def singleton(cls):
instances = {}
def getinstance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return getinstance
@singleton
class my_cls(object):
pass
这个实现单例模式的方式将原来类的定义隐藏在闭包函数中,通过闭包函数及其中引用的自由变量来控制类对象的生成。由于唯一的实例存放在自由变量中,而且自由变量是无法直接在脚本层进行访问的。这种方式非常隐蔽的保护实例不被修改,因此很适合用于单例模式。
这种方式简单明了,很容易实现。但是如果不了解闭包实现过程和变量的绑定等概念可能会不明白其实现的过程。建议参考一下我的另一篇博文:理解 python 闭包概念。
这里一个很有趣的地方是为什么要使用 instances = {}这样一个变量?可不可以不用字典,使用 instance
= None?如果 singleton 作为装饰器被多个不同的类使用,那么 instance 中会存在几个不同的实例么?
有时间可以思考一下这几个问题,答案也可以在我写的闭包相关的博文中找到。
3.元类方式
所谓单例模式,即我们需要控制类实例的生成过程,并且保证全局只可能存在一个唯一的实例。既然需要在创建类的对象过程中做些什么,应该很容易想到元类。
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
完全合法)。
4.类作为装饰器之__call__
方式
不仅函数可以作为装饰器,类也可以作为装饰器。
下面简单的介绍一下使用类作为装饰器实现单例模式的另一种方式。
'''
遇到问题没人解答?小编创建了一个 Python 学习交流 QQ 群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和 PDF 电子书!
'''
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
函数作为装饰器返回的是一个函数,函数被调用过程中实际上是间接地调用其内部包裹的被装饰的对象。
类作为装饰器要想达到相同的效果只需要将类的对象返回,并且其对象是可以调用的。这是上面这个例子表达的一个核心思想。
这种方式写法很多,也很灵活,其思想基本上就是对被包裹对象的调用实际上调用的是类对象的__call__
函数,该函数实际上是对被装饰对象的一次封装。
5.类本身实现方式
上面的例子中我们都是使用的装饰器或者元类的方式间接的通过控制类对象生成的方式来保证对象的唯一性,那么有没有办法直接在类中通过某种方式保证类对象的唯一性?
答案是肯定的。参考我之前写的一篇介绍元类的文章,可知生成对象前会调用函数__new__
,如果__new__
函数返回被创建的对象,那么会自动调用类中定义的__init__
函数进行对象的初始化操作。
相信读了上面这句话,应该知道我们接下来要干什么了?没错,我们的目标就是__new__
。
class MSC(object):
_INSTANCE = None
def new(cls, *args, **kwargs):
if not cls._INSTANCE:
cls._INSTANCE = super(MSC, cls).new(cls, *args, **kwargs)
cls._INSTANCE.args = args
cls._INSTANCE.kwargs = kwargs
return cls._INSTANCE
def init(self, *args, **kwargs):
pass
在这个例子中,我们完全可以理解为什么只会有一个类的对象会被创建。这种方式的定义决定了类本身只能被创建一个对象。
但是这里有一点需要注意,那就是不管创建多少 MSC 的对象,至始至终只会有一个对象,但是如果每次创建的时候传入的参数都不同,也就是__init__
函数中参数不同,会导致同一个对象被多次初始化。
这种方式的弊端显然很明显,那就是该方法只能作用于单个类的定义。不能像上面的装饰器和元类,一次实现,可以到处使用。
那能不能将这个控制类生成过程的结构单独抽象出来呢?而且有没有什么方法能防止同一个对象多次被__init__
初始化。下面我们看一种能被不同的类使用的更加抽象的结构。
最后
Python 崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS 等更加高级的领域。Python 可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python 可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
??Python 所有方向的学习路线??
Python 所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
??Python 必备开发工具??
工欲善其事必先利其器。学习 Python 常用的开发软件都在这里了,给大家节省了很多时间。
??Python 全套学习视频??
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
??实战案例??
学 python 就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。
因此在学习 python 的过程中一定要记得多动手写代码,教程只需要看一两遍即可。
??大厂面试真题??
我们学习 Python 必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
评论