python单例模式

1. 什么是单例模式

定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。我们希望在系统中一个类只出现一个实例。

2. 应用场景

  · 需要频繁实例化然后销毁的对象。每次实例化都会开辟新内存,频繁实例化将产生大量内存开销。
  · 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  · 有状态的工具类对象。
  · 频繁访问数据库或文件的对象。
  · 生成全局惟一的序列号。
  · 访问全局复用的惟一资源,如磁盘、总线等。
  · 单个对象占用的资源过多,如数据库等。
  · 系统全局统一管理,如Windows下的Task Manager。
  · 网站计数器。
  再举个熟悉的例子:python中的模块它就是个单例模式设计。当模块在第一次导入时会生成.pyc文件(在__pycache__文件夹下),当第二次再执行导入这个模块操作时它就会直接加载.pyc文件,而不再会去操作这个模块本身。

3. 单例模式优缺点

优点:
  · 在内存中只有一个对象,节省内存空间。
  · 避免频繁的创建销毁对象,可以提高性能。
  · 可以全局访问。
  · 全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用。
缺点:
  · 开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然会产生一些开销。可以通过使用静态初始化解决此问题。
  · 可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
  · 对象生存期:不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。

4. python单例模式实现:重写__new__()方法

  首先我们来了解一下python中对象的创建过程。以下给个例子:

class Animal(object):
	def __init__(self):
		pass

	def run(self):
		pass


class Bird(Animal):
	def __init__(self, color):
		self.color = color

	def fly(self):
		pass

b1 = Bird('red') # 语句1

  当执行语句1去创建b1这个对象时,python类内部创建实例过程大致是先执行__new__()方法创建对象;然后执行__init__()方法初始化这个对象;最后如果对象没用了系统自动调用__del__()方法来释放这个对象。具体的,对于这个例子:
  先是寻找Bird类中的__new__()方法来创建对象但是由于它没有定义,那么python默认就往上溯去找其父类Anlimal的__new__()方法,发现它也没有,继续上溯直到object这个基类,这个基类对__new__()方法是有定义的。它是这么定义的:

class object:
    """ The most base type """
    ...

    @staticmethod # known case of __new__
    def __new__(cls, *more): # known special case of object.__new__
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass
    
    ...

  从这里可以看出object__new__()它干的事是创建并返回一个新的对象(即任何时候你只要调用object的new它就会去新开一个内存来新建一个对象并返回该对象。有人说,不返回会怎样:如果你不返回对象那么就不会执行__init__(),因为self变量没有值)。当调用完__new__()后就会产生对象然后将这个对象返回传递给self变量,去执行__init__()函数进行初始化,那么至此一个对象就建好了。当然最后对象销毁时会自动调用当前类中的__del__()方法,如果当前类没定义则找直接父类的__del__()方法直到回溯到object的__del__()方法,object对__del__()是有定义的。这里顺便提一下,使用对象通过点调用属性或方法它的优先级是这样的:先搜索当前类的属性或方法,若有则执行,若没有就搜索其直接父类,如果直接父类有就执行直接父类的,若直接父类没有就再依次类推往上溯直到找到这个属性或方法如果到了最上层类还没有就抛没有该属性或方法错误。
  所以如果我们想只出现一个实例完全可以在当前类中重写__new__()方法,我只需让它只创建一个实例对象即可。而不是用默认情况下的调用object__new__()方法,因为它是始终都会新开内存来新建一个对象。所以我们实现单例的方法就是在当前类中自己定义__new__()方法,让它只能新建一次类对象,此时它将重写父类中已有的__new__()方法。具体给出代码如下:

class Bird(Animal):
	__object = None # 存放第一次新建的对象,这也将是唯一的一个实例对象
	__flag = True
	def __new__(cls, *args, **kwargs):
		'''
		对象间的联系可以依赖类属性,用类属性来判断是否已经新建了对象。
		如果已经新建过了就直接返回之前新建的对象,若没有就调object的new新建一个,
		从而来保证至始至终只有一个对象实例。
		'''
		if not cls.__object:
			cls.__object = object.__new__(cls)
		return cls.__object

	def __init__(self, color):
		'''
		为了保证初始化也只执行一次,这里给个逻辑判断来阻断多次操作
		'''
		if Bird.__flag:
			print('init...')
			self.color = color
			Bird.__flag = False
	
	def fly(self):
		pass

b1 = Bird('red')
b2 = Bird('black')
if id(b1) == id(b2):
	print('恭喜!是单例咯!', b1.color, b2.color)
else:
	print('抱歉!还不是单例哦!')

结果

5. 单例模式注意事项

  · 不要做断开单例类对象与类中静态引用的危险操作。
  · 多线程使用单例使用共享资源时,注意线程安全问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值