在软件设计模式中,经常会使用到单例模式,也就是保证一个系统中只有一个实例。比方说在服务器中,每个用户都需要频繁访问一个对象,那么每次访问的时候就需要建立一个实例,那么这个时候就需要用到单例模式。确保一个类中只有一个实例,从而方便对实例个数的控制并节约系统资源。
你可能会问,那为什么不直接定义一个全局变量,那也可以确保对象都可以被访问啊。虽然定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
利用__new__实现单例
在class类中,我们都会定义init方法,这个方法是初始化方法,而在它的背后还有一个new方法,也就是先创建,再初始化。类似C++中的构造函数。
当我们需要实现单例模式的时候,就可以重写其中的new方法了。思想很简单:给Base类赋予了一个instance属性,如果instance属性为None则创建实例对象,并使instance属性引用(指向)该对象,否则直接返回instance所引用的对象。因此,最后代码中的两个实例实际上引用了同一个内存对象。
class Base():
instance = None
def __new__(cls, *args, **kargs):
if cls.instance is None:
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self):
pass
aa = Base()
bb = Base()
print(id(aa))
print(id(bb))
利用元类中的__call__实现单例
在Python中,一切皆对象,元类是创建类的类,因此类实际上是元类的实例对象。也就是类属于对象,而实例化出来的对象属于类。
在Python中,如果一个对象定义了__call__方法,那么该对象为可调用对象,意思是可以用调用函数的形式来调用对象。
也就是只有类A被创建后才能创建类A的对象,因此为了先创建出类A,SingletonMetaClass的__new__和__init__方法会首先被执行。当执行语句A()创建类A的对象时,根据__call__方法的定义,由于类A是元类SingletonMetaClass的对象,所以元类SingletonMetaClass的__call__方法会调用。
class SingleMetaClass(type):
_instance = None
def __call__(self, *args, **kwargs):
print('MetaClass.__call__ called')
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
class A(metaclass = SingleMetaClass):
pass
a1 = A()
a2 = A()
print(a1)
print(a2)
利用装饰器实现单例模式
装饰器本身是返回函数对象,我们可以通过使用装饰器给函数增加功能而不添加更改函数的代码。在以下个案中,我们在装饰器中添加一个字典类型的自由变量_instance;然后在闭包中判断类名是否存在于_instance中,如果不存在则创建一个类的事例,并讲其添加到字典中;如果存在则不进行实例化,之后直接返回字典中的实例。
def decorator_Single(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@decorator_Single
class B(object):
def __init__(self):
pass
b1 = B()
b2 = B()
print(id(b1))
print(id(b2))
#
B = decorator_Single(B)
b3 = B()
print(id(b3))
另外,调用装饰器函数中的方法有两个,一个是使用@decorator_Single,另一个则是用装饰器函数修饰该函数,并将该函数返回给它本身。也就是:B = decorator_Single(B),因此也可以看出:函数装饰器可以嵌套。
简单说下带参数的装饰器
在分析 函数装饰器和被修饰函数的关系时,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那怎么办?
那么就在其中再嵌套一层就可以了。
def outermost(*args):
def out(func):
print ("装饰器参数{}".format(args))
def inner(*args):
print("innet start")
func(*args)
print ("inner end")
return inner
return out
@outermost("我是装饰器参数")
def myfun(*args):
print ("试试装饰器和函数都带参数的情况,被装饰的函数参数{}".format(args))
myfun("我是被装饰的参数")