我们常说的单例模式是个什么

在软件设计模式中,经常会使用到单例模式,也就是保证一个系统中只有一个实例。比方说在服务器中,每个用户都需要频繁访问一个对象,那么每次访问的时候就需要建立一个实例,那么这个时候就需要用到单例模式。确保一个类中只有一个实例,从而方便对实例个数的控制并节约系统资源。

你可能会问,那为什么不直接定义一个全局变量,那也可以确保对象都可以被访问啊。虽然定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

利用__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("我是被装饰的参数")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值