Python学习笔记:单例模式
单例设计模式:单例模式提供这样一个机制,确保类有且只有一个特定类型的对象,并提供全局访问点。一般单例模式使用时,需要注意以下几点:
- 确保该类有且只有一个对象被创建。
- 需要为该对象提供一个全局访问点,以便程序可以全局访问该对象(必须保证这个访问点返回的都是同一个对象)。
- 需要注意控制共享资源的访问。
单例模式优点:如果在程序中某个对象只需要创建一次,就可以考虑使用单例模式,比如日志操作,数据库操作等,这种对象创建多个就会非常浪费资源,也不能很好的实现这些操作的同步,所以单例模式是一个非常不错的选择。
单例模式缺点:它的缺点也很明显,因为单例是全局变量,所以使用时它可能在某个地方已经被修改了,但是开发人员可能并不知道。而且所有使用到这个单例的类,会因为这个单例成为紧密耦合的关系,从而某个类的改变可能会影响到其他的类。
实现方式:一般有两种方式,一种是使用元类metaclass控制类实例化时的对象,另一种是使用类的__new__方法控制类返回的对象。(关于元类,这里有篇博客讲得很详细:https://www.cnblogs.com/tkqasn/p/6524879.html)
元类实现单例模式(Python3.6):
class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs) def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs) return cls.__instance class MySingleton(metaclass=Singleton):
def __init__(self, val):
self.val = val
print(self.val) hello = MySingleton('hello')
hi = MySingleton('hi')
print(hello is hi) ----------输出结果----------
hello
True
- metaclass:Python3中metaclass是通过指定metaclass实现的,Python2中是通过指定类变量__metaclass__来实现的,但原理都是一样的。
- type:Python中所有的类都是type类的实例,即一个类(还未实例化)的定义,其实就是type类(Python内建元类)的实例。如下的打印可以更加直观的理解这一点:
>>> int.__class__
<class 'type'>
>>> num = 3
>>> num.__class__
<class 'int'>
>>> num.__class__.__class__
<class 'type'>
>>>
>>>
>>> class A:
pass >>> A.__class__
<class 'type'>
>>> a = A()
>>> a.__class__
<class '__main__.A'>
>>> a.__class__.__class__
<class 'type'>
>>> - __call__:当调用一个实例时,即执行实例加括号的形式,就会调用该实例的__call__方法,如果没有定义(需要自己定义),则会报错。例如a=A(),a()就会调用a的__call__方法。
- 代码执行流程:第一步执行MySingleton时(即没加括号的部分),进行元类的实例化,即MySingleton=Singleton(),Singleton的实例化和普通类一样会先执行__new__返回该类的实例,然后自动执行该实例的__init__方法进行初始化,此示例中的初始化方法给该实例赋予了一个值为None的__instance变量;第二步执行MySingleton('hello')时,进行类的实例化,即MySingleton('hello')=Singleton()('hello'),这里就会调用到Singleton的__call__方法了,而super().__call__即调用type的__call__方法,这时候就和普通类实例化一样会调用MySingleton的__new__和__init__方法了。
- 原理:由于每次实例化MySingleton时都会先调用metaclass中的__call__方法,所以只有第一次实例化时才会执行MySingleton的__new__和__init__,后面的实例化都只会返回第一次实例化好的实例,所以导致的结果就是无论进行多少次实例化,都给你返回同一个实例,当然就只有单例了(所以“输出结果”中就没有打印“hi”了) 。
- cls和self:Singleton的编写,在eclipse中提示需要写成self,在PyCharm中提示需要写成cls,因为参数self是约定代表实例本身,但在这里type的实例就是类,所以推荐写成cls。
__new__实现单例模式(Python3.6):
class MySingleton:
def __init__(self, val):
self.val = val
4 print(self.val)
5 print(self.__dict__) def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(cls) return cls._instance hello = MySingleton('hello')
hi = MySingleton('hi')
print(hello is hi) -----------输出结果--------------
hello
{'val': 'hello'}
hi
{'val': 'hi'}
True
- 原理:通过给类定义一个类变量,指向本类的一个实例,每次实例化调用__new__的时候都返回这个类变量,可以看到数据结果打印的是True,所以自然就是单例了。
- 注意:每次实例化虽然都是同一个实例,但是每次实例化都会调用一次__init__方法,导致这个实例会随着每次初始化而改变,所以这种方式来实现单例就看情况和自己的需求来使用了。
- hasattr:类中的私有变量,即加了双下划线的变量,在__dict__中会加上一个“_classname”前缀,所以如果这里使用__instance的话,hasattr(cls, '__instance')会一直返回False,因为这里已经不是__instance了,而是_MySingleton__instance。
Python学习笔记:单例模式的更多相关文章
- python学习笔记整理——字典
python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...
- VS2013中Python学习笔记[Django Web的第一个网页]
前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...
- python学习笔记之module && package
个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...
- python学习笔记(六)文件夹遍历,异常处理
python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...
- python学习笔记--Django入门四 管理站点--二
接上一节 python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...
- python学习笔记--Django入门0 安装dangjo
经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...
- python学习笔记(一)元组,序列,字典
python学习笔记(一)元组,序列,字典
- Pythoner | 你像从前一样的Python学习笔记
Pythoner | 你像从前一样的Python学习笔记 Pythoner
- OpenCV之Python学习笔记
OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...
- python学习笔记(五岁以下儿童)深深浅浅的副本复印件,文件和文件夹
python学习笔记(五岁以下儿童) 深拷贝-浅拷贝 浅拷贝就是对引用的拷贝(仅仅拷贝父对象) 深拷贝就是对对象的资源拷贝 普通的复制,仅仅是添加了一个指向同一个地址空间的"标签" ...
随机推荐
- Java中的阻塞队列-SynchronousQueue
SynchronousQueue是一个不存储元素的阻塞队列.每一个put操作必须等待一个take操作,否则不能继续添加元素.SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的 ...
- DXperience Winforms新版本13.2功能预览
据界面控件厂商 DevExpress 官方最新消息,大家期盼已久的DXperience 13.2终于要面世了.今天在这里提前跟大家提前披露一下DXperience Winforms 13.2的一些精彩 ...
- 视频会议20方100点 v2.66.1.18
平台: Windows 类型: 虚拟机镜像 软件包: 视频会议服务器( Video Conference Server ) 20-party video conference business int ...
- powershell解决win10开始菜单和通知中心无法打开
然后通过 Ctrl + Shift + Esc 弹出任务管理器点击文件-->运行新任务 在打开的填写框里面输入 "powershell"同时勾选下方的"以管理员身份 ...
- 利用PCHunter结束各种进程
http://www.epoolsoft.com/ 经测试,可结束主动防御等.
- 字符串反转,栈模拟(ZOJ1151)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=151 这里可以用栈模拟,也可以用STL,reverse();函数. 但 ...
- node入口文件分析和目录初始化
1.需要安装的模块 npm install express npm install jade npm install mongoose npm install bower -g npm install ...
- CKEditor4x word导入不保存格式的解决方案
后台上传文档时,目前功能都通过word直接复制黏贴实现,之前和word控件朋友一起测试找个问题,原始代码CK4.X没有找个问题. 第一时间排查config.js的配置发现端倪,测试解决! 由于配合ck ...
- 2018.8.1 Java中的反射和同步详解
为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他 ...
- HTML5<fieldset>标签
1.<fieldset>标签对表单中的相关元素进行分组. 2.<fieldset>标签会在相关表单元素周围绘制边框. <!DOCTYPE html><html ...