相关代码已经上传至Github:Python_Development_Interview,大家可以收藏专题-Python的设计模式:解密+实战,之后会持续更新相关的设计模式。

1. 认识单例模式

  • 认识单例模式
  • 1 单例模式含义
  • 2 单例模式优点
  • 3 单例模式缺点
  • 4 单例模式应用

2. Python实现单例模式

  • Python实现单例模式
  • 1 多种实现方法
  • 2 实例分析

3. 总结

  • 总结

认识单例模式

1.1 单例模式含义

例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

1.2 单例模式优点

单例模式的优点: 1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间; 2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用; 3、单例可长驻内存,减少系统开销。

1.3 单例模式缺点

单例模式的缺点 1、单例模式的扩展是比较困难的; 2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到); 3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试; 4、单例模式在某种情况下会导致“资源瓶颈”。

1.4 单例模式应用

单例模式的应用举例: 1、生成全局惟一的序列号; 2、访问全局复用的惟一资源,如磁盘、总线等; 3、单个对象占用的资源过多,如数据库等; 4、系统全局统一管理,如Windows下的Task Manager; 5、网站计数器。

Python实现单例模式

2.1 多种实现方法

2.1.1.使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

singleton_by_module.py

class Singleton(object):def foo(self):pass
singleton = Singleton()

将上面的代码保存在文件singleton_by_module.py中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象test_singleton_by_module.py

from singleton_by_module import Singletont = Singleton()

这样我们一旦调用到singleton_by_module.py就会产生一个singleton_by_module.pyc,以后我们每次调用都会直接引用这里面的代码。

2.1.2.使用装饰器

singleton_by_decorator.py

def Singleton(cls):_instance = {}count = 0def _singleton(*args, **kargs):nonlocal countif cls not in _instance:print(f"count: {count}: {cls.__name__} not init")_instance[cls] = cls(*args, **kargs)else:print(f"count: {count}: {cls.__name__} alreay init")count+=1return _instance[cls]return _singleton@Singleton
class A(object):a = 1def __init__(self, x=0):self.x = xa1 = A(2)
a2 = A(3)print(f"a1 id: {id(a1)}, a1 value: {a1.x}")
print(f"a2 id: {id(a2)}, a2 value: {a2.x}")### output
count: 0: A not init
count: 1: A alreay init
a1 id: 140536039677232, a1 value: 2
a2 id: 140536039677232, a2 value: 2

根据上面的运行情况,我们可以发现,当a1被创建后调用的是正常的产生实例的过程,当a2被创建的时候,由于之前实例已经被存储下来,所以直接引用了a1的实例,所以他们的id是一样的,也就是他们引用了同一个内存实例。

2.1.3.使用类

singleton_by_class.py

class Singleton:def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancea1 = Singleton.instance()
a2 = Singleton.instance()print(f"a1 id: {id(a1)}")
print(f"a2 id: {id(a2)}")### output
a1 id: 140419818871776
a2 id: 140419818871776

一般情况,大家以为这样就完成了单例模式,但是这样当使用多线程时会存在问题

singleton_by_class_mutli_threading.py

class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instanceimport threadingdef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()

程序执行后,打印结果如下:

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

看起来也没有问题,那是因为执行速度过快,如果在init方法中有一些IO操作,就会发现问题了,下面我们通过time.sleep模拟

我们在上面init方法中加入以下代码:

singleton_by_class_mutli_threading_sleep.py

def __init__(self):import timetime.sleep(1)

重新执行程序后,结果如下

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

问题出现了!按照以上方式创建的单例,无法支持多线程

解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全

singleton_by_class_mutli_threading_lock.py

import time
import threading
class Singleton:_instance_lock = threading.Lock()def __init__(self):time.sleep(1)@classmethoddef instance(cls, *args, **kwargs):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)
for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

打印结果如下:

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行:

@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance

这样,一个可以支持多线程的单例模式就完成了

singleton_by_class_mutli_threading_safe.py

import time
import threading
class Singleton:_instance_lock = threading.Lock()def __init__(self):time.sleep(1)@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)
for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)完整代码

这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()

如果用 obj=Singleton() ,这种方式得到的不是单例

2.1.4基于new方法实现(推荐使用,方便)

通过上面例子,我们可以知道,当我们实现单例时,为了保证线程安全需要在内部加入锁

我们知道,当我们实例化一个对象时,是先执行了类的new方法(我们没写时,默认调用type.new),实例化对象;然后再执行类的init方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式

singleton_by_new.py

import threading
class Singleton:_instance_lock = threading.Lock()def __init__(self):passdef __new__(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = super(Singleton,cls).__new__(cls,*args, **kwargs)return Singleton._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)def task(arg):obj = Singleton()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()

打印结果如下:

<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>

采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()

2.1.5.基于metaclass方式实现

相关知识

""" 1.类由type创建,创建类时,type的init方法自动执行,类() 执行type的call方法(类的new方法,类的init方法) 2.对象由类创建,创建对象时,类的init方法自动执行,对象()执行类的call方法 """

class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):passobj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。obj()    # 执行Foo的 __call__ 方法

元类的使用metaclass_ex.py

class SingletonType(type):def __init__(self,*args,**kwargs):super(SingletonType,self).__init__(*args,**kwargs)def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类print('cls',cls)obj = cls.__new__(cls,*args, **kwargs)cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)return objclass Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonTypedef __init__(self,name):self.name = namedef __new__(cls, *args, **kwargs):return object.__new__(cls)obj = Foo('xx')

实现单例模式

singleton_by_metaclass.py

import threadingclass SingletonType(type):_instance_lock = threading.Lock()def __call__(cls, *args, **kwargs):if not hasattr(cls, "_instance"):with SingletonType._instance_lock:if not hasattr(cls, "_instance"):cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)return cls._instanceclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = nameobj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)

2.2 实例分析

总线是计算机各种功能部件或者设备之间传送数据、控制信号等信息的公共通信解决方案之一。现假设有如下场景:某中央处理器(CPU)通过某种协议总线与一个信号灯相连,信号灯有64种颜色可以设置,中央处理器上运行着三个线程,都可以对这个信号灯进行控制,并且可以独立设置该信号灯的颜色。抽象掉协议细节(用打印表示),如何实现线程对信号等的控制逻辑。 加线程锁进行控制,无疑是最先想到的方法,但各个线程对锁的控制,无疑加大了模块之间的耦合。下面,我们就用设计模式中的单例模式,来解决这个问题。

代码如下:

import threading
import time
#这里使用方法__new__来实现单例模式
class Singleton(object):#抽象单例def __new__(cls, *args, **kw):if not hasattr(cls, '_instance'):orig = super(Singleton, cls)cls._instance = orig.__new__(cls, *args, **kw)return cls._instance
#总线
class Bus(Singleton):lock = threading.RLock()def sendData(self,data):self.lock.acquire()time.sleep(3)print "Sending Signal Data...",dataself.lock.release()
#线程对象,为更加说明单例的含义,这里将Bus对象实例化写在了run里
class VisitEntity(threading.Thread):my_bus=""name=""def getName(self):return self.namedef setName(self, name):self.name=namedef run(self):self.my_bus=Bus()self.my_bus.sendData(self.name)if  __name__=="__main__":for i in range(3):print "Entity %d begin to run..."%imy_entity=VisitEntity()my_entity.setName("Entity_"+str(i))my_entity.start()

运行结果如下: Entity 0 begin to run... Entity 1 begin to run... Entity 2 begin to run... Sending Signal Data... Entity_0 Sending Signal Data... Entity_1 Sending Signal Data... Entity_2 在程序运行过程中,三个线程同时运行(运行结果的前三行先很快打印出来),而后分别占用总线资源(后三行每隔3秒打印一行)。虽然看上去总线Bus被实例化了三次,但实际上在内存里只有一个实例。

总结

因为单例模式在设计模式中算是最基础且最简单的一个模式,因此在一般初级面试的时候,面试官都会通过这个问题来考察,一个很重要的原因是单例模式实现方法多种且优化的方式也有很多,所以也很能考察应聘者的水平,所以,大家要好好学这个最基础的设计模式啊!另外,在Java中单例模式常说的饱汉饿汉模式,其实和Python中的利用__new__和利用class来创建是一样的,也就是在什么时候创建实例的区别。

print python 如何加锁_深度解密Python单例模式相关推荐

  1. 以下哪个选项不是单例模式的优点_深度解密Python单例模式

    相关代码已经上传至Github:Python_Development_Interview,大家可以收藏专题-Python的设计模式:解密+实战,之后会持续更新相关的设计模式. 1. 认识单例模式 认识 ...

  2. python切片原理_深度解析Python切片

    详解Python 切片语法 Python的切片是特别常用的功能,主要用于对列表的元素取值.使用切片也会让你的代码显得特别Pythonic. 切片的主要声明如下,假设现在有一个list,命名为alist ...

  3. grad在python什么模块_深度学习(Deep Learning)基础概念1:神经网络基础介绍及一层神经网络的python实现...

    此专栏文章随时更新编辑,如果你看到的文章还没写完,那么多半是作者正在更新或者上一次没有更新完,请耐心等待,正常的频率是每天更新一篇文章. 该文章是"深度学习(Deep Learning)&q ...

  4. 深度解密 Python 列表的实现原理

    这是「进击的Coder」的第 751 篇技术分享 作者:古明地觉 来源:古明地觉的编程教室 " 阅读本文大概需要 13 分钟. " 楔子 最近有读者发了一条私信,希望我介绍一下 P ...

  5. python attention机制_[深度应用]·Keras实现Self-Attention文本分类(机器如何读懂人心)...

    [深度应用]·Keras实现Self-Attention文本分类(机器如何读懂人心) 笔者在[深度概念]·Attention机制概念学习笔记博文中,讲解了Attention机制的概念与技术细节,本篇内 ...

  6. python怎么避免浅拷贝_深度解读Python深拷贝与浅拷贝问题

    Illustrations by Leon Tukker ♚ 作者:PayneLi,Python全家桶,主要讲述数据挖掘.机器学习和深度学习领域的前沿技术,同时还会推荐一些行业最新论文.技术专家的经验 ...

  7. python垃圾回收价格表_深度解析Python垃圾回收机制(超级详细)

    我们知道,目前的计算机都采用的是图灵机架构,其本质就是用一条无限长的纸带,对应今天的存储器.随后在工程学的推演中,逐渐出现了寄存器.易失性存储器(内存)以及永久性存储器(硬盘)等产品.由于不同的存储器 ...

  8. python 判断类型_青少年之Python编程课程安排lt;第一季gt;

    第一章    开启Python之旅 1.   你将了解什么是Python 2.   在电脑上安装并简单使用Python 3.   开始通过Python与计算机进行交流(编程) 第二章    变量 1. ...

  9. python求加速度_如何利用Python 为自然语言处理加速度

    自去年发布 Python 的指代消解包(coreference resolution package)之后,很多用户开始用它来构建许多应用程序,而这些应用与我们最初的对话应用完全不同. 利用 spaC ...

最新文章

  1. 一文了解四种软件架构:Serverless架构、微服务架构、分布式架构、单体架构
  2. C#中throw抛出异常后,后续代码执行情况
  3. 详解 Java 中的三种代理模式
  4. SAP ABAP用户锁定标识USR02-UFLAG
  5. 【Java NIO深入研究3】文件锁
  6. HashSet 与HashMap底层实现
  7. ncnn数据归一化和结果解析
  8. 【Debug】IAR在线调试时报错,Warning: Stack pointer is setup to incorrect alignmentStack,芯片使用STM32F103ZET6...
  9. 查看服务器文件命令大全,查看服务器文件的命令
  10. 数据库系统 图书管理系统 C语言
  11. 计算机主板的工作原理,计算机主板工作原理介绍
  12. xp镜像文件的product key
  13. LINUX(CENTOS7.X)SVN部署文档+pycharmSvn
  14. 大咖说开源|郑振宇:通过开源手段巩固基础软件供应链
  15. BUG生命周期和管理
  16. 【学透二叉树-二叉搜索树(二叉树)的最近公共祖先】
  17. 写在Groovy/Grails栏目开通的话
  18. 晶联讯LCD(JLX128128G-610-PC)使用教程
  19. mongodb关联表查询
  20. SystemVerilog学习笔记1 ---《数据类型》

热门文章

  1. Emacs高亮设置:Hi-Lock mode笔记
  2. libevent安装总结 - jinfg2008的专栏 - 博客频道 - CSDN.NET
  3. 2013年4月27日星期六
  4. Oracle Compile 编译 无效对象(ORA-04063: package body SYS.DBMS_XPLAN 有错误)
  5. 《CLR Via C# 第3版》笔记之(十九) - 任务(Task)
  6. 艾伟_转载:ASP.NET MVC数据验证
  7. 【转载】2009年春晚节目单提前揭晓
  8. python的函数的对象属性_Python帮助函数调试函数 用于获取对象的属性及属性值...
  9. 【测试】批量删除供应商配额(Quota )
  10. 【ABAP】 屏幕图片显示Demo