认识单例模式

含义

单例模式是一种常用的软件设计模式,在应用这个模式时,类只会生成一个实例对象。
换句话说,单例模式确保某个类有且仅有一个实例,而且自行实例化并向整个系统提供这个实例,当我们在程序中的不同位置调用这个类进行实例化,如果类的实例不存在,会创建一个实例;如果已存在就会返回这个实例。
比如回收站就是单例模式的应用,我们电脑桌面上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都是使用这个唯一的实例。

优点

  • 因为单例模式在全局内只有一个实例,因此可以节省比较多的内存空间。
  • 全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用。

Python实现单例模式

使用装饰器方式实现

函数装饰器方式

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 = aa1 = A(1)
a2 = A(2)
# id()函数可以获取对象的内存地址,同一内存地址即为同一对象
print(id(a1), id(a2))

类装饰器方式

class Singleton(object):def __init__(self, cls):self._cls = clsself._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):passb1 = B()
b2 = B()
print(id(b1), id(b2))

关于装饰器,我在之前的文章就有介绍Python装饰器,读完这篇你就懂了,感兴趣的小伙伴可以看一下。

使用类的方式实现

class Singleton(object):def __init__(self, *args, **kwargs):pass@classmethoddef get_instance(cls, *args, **kwargs):# hasattr() 函数用于判断对象是否包含对应的属性 , 这里是看看这个类有没有 _instance 属性  if not hasattr(Singleton, '_instance' ):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instances1 = 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()方法创建。 再者,当使用多线程时这样也会存在问题,我们来看下面的代码:

class Singleton(object):def __init__(self, *args, **kwargs):import timetime.sleep(1)@classmethoddef get_instance(cls, *args, **kwargs):# hasattr() 函数用于判断对象是否包含对应的属性 , 这里是看看这个类有没有 _instance 属性if not hasattr(Singleton, '_instance'):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task():obj = Singleton.get_instance()print(obj)for i in range(10):t = threading.Thread(target=task)t.start()

程序执行后,打印结果:

<__main__.Singleton object at 0x031014B0>
<__main__.Singleton object at 0x00DA32F0>
<__main__.Singleton object at 0x03101430>
<__main__.Singleton object at 0x03101530>
<__main__.Singleton object at 0x031015B0>
<__main__.Singleton object at 0x031016B0>
<__main__.Singleton object at 0x03101630>
<__main__.Singleton object at 0x03101830>
<__main__.Singleton object at 0x03101730>
<__main__.Singleton object at 0x031017B0>Process finished with exit code 0

如果在__init__()方法方法中有一些IO操作(此处使用time.sleep(1)来模拟),就会发现此时并不是同一个实例对象,这是因为在一个对象创建的过程中,会先去获取_instance属性,判断之前有没有实例对象,因为IO耗时操作,当他们判断的时候,还没有对象完成实例化,所以就会调用init()方法进行实例化,结果就是调用了多次,然后就创建了多个对象。那要如何解决呢? 答案是加锁,在获取对象属性_instance的时候加锁,如果已经有人在获取对象了,其他的人如果要获取这个对象,就先等一下,因为前面的那个人,可能在创建对象,就是还没创建完成。 代码如下:

class Singleton(object):_instance_lock = threading.Lock()  # 线程锁def __init__(self, *args, **kwargs):import timetime.sleep(1)@classmethoddef get_instance(cls, *args, **kwargs):with Singleton._instance_lock:# hasattr() 函数用于判断对象是否包含对应的属性 , 这里是看看这个类有没有 _instance 属性if not hasattr(Singleton, '_instance'):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance

但是为了保证线程安全,在类内部加入锁机制,又会使加锁部分代码串行执行,速度降低。

使用__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._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)

当python实例化一个对象时,是先执行类的__new__()方法,当我们没写__new__()方法时,默认调用基类object的__new__()方法,然后再执行类的__init__()方法,对这个对象进行初始化,所有我们可以基于这个,去实现单例模式,我们通过hasattr(Singleton,  **"_instance"** )(其中hasattr()的功能是判断一个对象有没有指定的属性)去判断之前有没有实例化过对象,如果有,就直接返回,没有就新创建一个。

附上控制台输出:可以看出,同样实现了单例。

但这样其实有个小问题,看输出其实执行了两遍__init__()方法,既然是同一个对象,初始化两次,这是不太合理的,我们可以改造一下:

class Singleton(object):def __init__(self):if not hasattr(Singleton, "_first_init"):print("__init__")Singleton._first_init = Truedef __new__(cls, *args, **kwargs):print("__new__")if not hasattr(Singleton, "_instance"):print("创建新实例")Singleton._instance = object.__new__(cls)return Singleton._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)

而且__new__()方法是支持多线程的,不需要单独再加线程锁进行规避操作,省时又省力,nice。

总结

本文章虽然是讲单例模式,但在实现单例模式的过程中,用到了挺多高级Python语法,包括装饰器、魔法函数__new__(),with语句块等等,感兴趣的小伙伴可以了解一下,其实Python还有挺魔力的,总是能够用较少的代码实现较多的功能。

看到最后记得点个关注哦!

Python实现单例模式的几种方式相关推荐

  1. python实现单例模式的三种方式及相关知识解释

    python实现单例模式的三种方式及相关知识解释 模块模式 装饰器模式 父类重写new继承 单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singlet ...

  2. python实现单例模式的几种方式_基于Python中单例模式的几种实现方式及优化详解...

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  3. Python 发送 email 的三种方式

    Python发送email的三种方式,分别为使用登录邮件服务器.使用smtp服务.调用sendmail命令来发送三种方法 本文原文自米扑博客:Python 发送 email 的三种方式 Python发 ...

  4. Py之qrcode:调用python的qrcode库两种方式生成二维码、带logo的二维码

    Py之qrcode:调用python的qrcode库两种方式生成二维码.带logo的二维码 目录 python编程实现生成二维码 1.第一种方式-纯文本 2.第二种方式-带logo

  5. Python 操作 MySQL 的5种方式(转)

    Python 操作 MySQL 的5种方式 不管你是做数据分析,还是网络爬虫,Web 开发.亦或是机器学习,你都离不开要和数据库打交道,而 MySQL 又是最流行的一种数据库,这篇文章介绍 Pytho ...

  6. 运用python的方式_对Python使用mfcc的两种方式详解

    1.Librosa import librosa filepath = "/Users/birenjianmo/Desktop/learn/librosa/mp3/in.wav" ...

  7. 聊聊 Python 调用 JS 的几种方式,你都知道吗?

    1. 前言 日常 Web 端爬虫过程中,经常会遇到参数被加密的场景,因此,我们需要分析网页源代码 通过调式,一层层剥离出关键的 JS 代码,使用 Python 去执行这段代码,得出参数加密前后的 Py ...

  8. python 下载文件-Python下载文件的11种方式

    原标题:Python下载文件的11种方式 在本教程中,你将学习如何使用不同的Python模块从web下载文件.此外,你将下载常规文件.web页面.Amazon S3和其他资源. 最后,你将学习如何克服 ...

  9. Python格式化字符串的4种方式

    文章目录 Python格式化字符串的4种方式 一:%号 二:str.format 三:f-Strings 四:标准库模板 五:总结四种方式的应用场景 Python格式化字符串的4种方式 一:%号 %号 ...

最新文章

  1. Scrum敏捷开发看板工具分享
  2. 中国AI科研产出全球第一 但引文影响力低
  3. 【译】Withdrawal symptoms
  4. 如何避免_旅游住宿如何避免雷区?
  5. Linux使用systemctl设置程序开机自启动
  6. Xcode7将无需开发者授权也能在真机上调试App
  7. 同时阅读多个pdf文档怎么办?
  8. javaweb学习总结(十)——HttpServletRequest对象(一)(转)
  9. 哪吒之魔童降世视听语言影评_豆瓣评分8.7,这个“新哪吒”不一般|《哪吒之魔童降世》影评...
  10. table固定表头行及列,其中行包含合并单元格
  11. pytorch自然语言处理之Pooling层的句子分类
  12. oci8 php,PHP_试用php中oci8扩展,给大家分享个php操作Oracle的操 - phpStudy
  13. 会员无损音乐各种格式转换成mp3等格式
  14. linux node安装菜鸟教程,Node.js 安装配置
  15. PayPal全力助推中国商家开拓海外市场
  16. springboot整合apache ftpserver详细教程(看这一篇就够了)
  17. 申请阿里云香港试用1年服务器教程
  18. web端微信二维码自定义样式
  19. 企业为什么要做等保?不做等保有什么后果?
  20. 一个简单的B站视频抽奖小程序

热门文章

  1. Mac下的pyecharts的安装及使用
  2. 广州大学计算机网络实验五,计算机网络实验五.doc
  3. 安然邮箱社交网络分析
  4. opensips安装及配置
  5. 计算机网络中各种命令的验证与使用
  6. 达观智能RPA机器人助力完善医疗行业信息化建设,解放医疗资源
  7. ireader android 源码,仿ireader书架 - androidCode的个人空间 - OSCHINA - 中文开源技术交流社区...
  8. linux下创建二进制文件并修改
  9. [激光器原理与应用-7]: 半导体制冷片与TEC温控器
  10. web渗透测试----34、DoS和DDoS攻击