Python实现单例模式的几种方式
认识单例模式
含义
单例模式是一种常用的软件设计模式,在应用这个模式时,类只会生成一个实例对象。
换句话说,单例模式确保某个类有且仅有一个实例,而且自行实例化并向整个系统提供这个实例,当我们在程序中的不同位置调用这个类进行实例化,如果类的实例不存在,会创建一个实例;如果已存在就会返回这个实例。
比如回收站就是单例模式的应用,我们电脑桌面上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都是使用这个唯一的实例。
优点
- 因为单例模式在全局内只有一个实例,因此可以节省比较多的内存空间。
- 全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用。
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__()
方法时,默认调用基类objec
t的__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实现单例模式的几种方式相关推荐
- python实现单例模式的三种方式及相关知识解释
python实现单例模式的三种方式及相关知识解释 模块模式 装饰器模式 父类重写new继承 单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singlet ...
- python实现单例模式的几种方式_基于Python中单例模式的几种实现方式及优化详解...
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Python 发送 email 的三种方式
Python发送email的三种方式,分别为使用登录邮件服务器.使用smtp服务.调用sendmail命令来发送三种方法 本文原文自米扑博客:Python 发送 email 的三种方式 Python发 ...
- Py之qrcode:调用python的qrcode库两种方式生成二维码、带logo的二维码
Py之qrcode:调用python的qrcode库两种方式生成二维码.带logo的二维码 目录 python编程实现生成二维码 1.第一种方式-纯文本 2.第二种方式-带logo
- Python 操作 MySQL 的5种方式(转)
Python 操作 MySQL 的5种方式 不管你是做数据分析,还是网络爬虫,Web 开发.亦或是机器学习,你都离不开要和数据库打交道,而 MySQL 又是最流行的一种数据库,这篇文章介绍 Pytho ...
- 运用python的方式_对Python使用mfcc的两种方式详解
1.Librosa import librosa filepath = "/Users/birenjianmo/Desktop/learn/librosa/mp3/in.wav" ...
- 聊聊 Python 调用 JS 的几种方式,你都知道吗?
1. 前言 日常 Web 端爬虫过程中,经常会遇到参数被加密的场景,因此,我们需要分析网页源代码 通过调式,一层层剥离出关键的 JS 代码,使用 Python 去执行这段代码,得出参数加密前后的 Py ...
- python 下载文件-Python下载文件的11种方式
原标题:Python下载文件的11种方式 在本教程中,你将学习如何使用不同的Python模块从web下载文件.此外,你将下载常规文件.web页面.Amazon S3和其他资源. 最后,你将学习如何克服 ...
- Python格式化字符串的4种方式
文章目录 Python格式化字符串的4种方式 一:%号 二:str.format 三:f-Strings 四:标准库模板 五:总结四种方式的应用场景 Python格式化字符串的4种方式 一:%号 %号 ...
最新文章
- Scrum敏捷开发看板工具分享
- 中国AI科研产出全球第一 但引文影响力低
- 【译】Withdrawal symptoms
- 如何避免_旅游住宿如何避免雷区?
- Linux使用systemctl设置程序开机自启动
- Xcode7将无需开发者授权也能在真机上调试App
- 同时阅读多个pdf文档怎么办?
- javaweb学习总结(十)——HttpServletRequest对象(一)(转)
- 哪吒之魔童降世视听语言影评_豆瓣评分8.7,这个“新哪吒”不一般|《哪吒之魔童降世》影评...
- table固定表头行及列,其中行包含合并单元格
- pytorch自然语言处理之Pooling层的句子分类
- oci8 php,PHP_试用php中oci8扩展,给大家分享个php操作Oracle的操 - phpStudy
- 会员无损音乐各种格式转换成mp3等格式
- linux node安装菜鸟教程,Node.js 安装配置
- PayPal全力助推中国商家开拓海外市场
- springboot整合apache ftpserver详细教程(看这一篇就够了)
- 申请阿里云香港试用1年服务器教程
- web端微信二维码自定义样式
- 企业为什么要做等保?不做等保有什么后果?
- 一个简单的B站视频抽奖小程序
热门文章
- Mac下的pyecharts的安装及使用
- 广州大学计算机网络实验五,计算机网络实验五.doc
- 安然邮箱社交网络分析
- opensips安装及配置
- 计算机网络中各种命令的验证与使用
- 达观智能RPA机器人助力完善医疗行业信息化建设,解放医疗资源
- ireader android 源码,仿ireader书架 - androidCode的个人空间 - OSCHINA - 中文开源技术交流社区...
- linux下创建二进制文件并修改
- [激光器原理与应用-7]: 半导体制冷片与TEC温控器
- web渗透测试----34、DoS和DDoS攻击