1. 认识单例模式

2. Python实现单例模式

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 Singleton

t = Singleton()

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

2.1.2.使用装饰器

singleton_by_decorator.py

def Singleton(cls):

_instance = {}

count = 0

def _singleton(*args, **kargs):

nonlocal count

if 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+=1

return _instance[cls]

return _singleton

@Singleton

class A(object):

a = 1

def __init__(self, x=0):

self.x = x

a1 = 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

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

a1 = 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

@classmethod

def instance(cls, *args, **kwargs):

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

import threading

def 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 time

time.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)

@classmethod

def instance(cls, *args, **kwargs):

with Singleton._instance_lock:

if not hasattr(Singleton, "_instance"):

Singleton._instance = Singleton(*args, **kwargs)

return Singleton._instance

def 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方法,改成下面的这样就行:

@classmethod

def 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)

@classmethod

def 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

def 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):

pass

def __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._instance

obj1 = 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):

pass

def __call__(self, *args, **kwargs):

pass

obj = 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 obj

class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType

def __init__(self,name):

self.name = name

def __new__(cls, *args, **kwargs):

return object.__new__(cls)

obj = Foo('xx')

实现单例模式

singleton_by_metaclass.py

import threading

class 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._instance

class Foo(metaclass=SingletonType):

def __init__(self,name):

self.name = name

obj1 = 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...",data

self.lock.release()

#线程对象,为更加说明单例的含义,这里将Bus对象实例化写在了run里

class VisitEntity(threading.Thread):

my_bus=""

name=""

def getName(self):

return self.name

def setName(self, name):

self.name=name

def 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..."%i

my_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来创建是一样的,也就是在什么时候创建实例的区别。

python单例模式解析_深度解密Python单例模式相关推荐

  1. print python 如何加锁_深度解密Python单例模式

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

  2. python 日期解析_如何在Python中解析ISO 8601格式的日期?

    python 日期解析 Python provides a datetime standard library which introduces datetime.isoformat(). As pe ...

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

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

  4. python文本解析_如何通过python进行文本解析?

    我希望使用python解析数据,以便将其导入Excel电子表格中.我需要一些帮助来实现过程的自动化.文件内容包括:ok: [wrt02.test1] => { "msg": ...

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

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

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

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

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

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

  8. python match函数_什么是python re.match函数?(实例解析)

    在这篇文章之中我们来了解一下关于python之中的正则表达式,有些朋友可能是刚刚接触到python这一编程语言,对于这一方面不是特别的了解,在接下来的文章之中我们来了解一下python中re.matc ...

  9. 文科生自学python要多久_怎么自学python,大概要多久?

    都让开!本人文科生,自学Python 2年半,作为一个曾经完全0基础,啥都不懂纯靠自学学会python的文科生,有一些不成熟的小建议可以分享一下. 首先不要觉着编程难,只要你认识26个英文字母,有一点 ...

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

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

最新文章

  1. java 模式匹配算法_用Java匹配模式
  2. javascript的null 和undifined
  3. 用python做双人五子棋_基于python的socket实现单机五子棋到双人对战
  4. 图片导入ppt后模糊_PPT设计,找图也是一种能力
  5. jenkins 执行构建 并查看结果
  6. 开源一周岁,MindSpore新特性巨量来袭
  7. 芯片漏洞攻击出现新变种,涉及英特尔SGX技术支持的应用
  8. 用winformz时间格式不正确_巨峰葡萄不能膨大?错!在正确的时间,用对方法,收获优质果穗型...
  9. Docker 踩坑之一:Error response from daemon: write *** : no space left on device
  10. python random设置种子_关于python:如何查询random.random()使用的种子?
  11. python可执行文件 情人节快乐_各位情人节快乐, Python帮忙撒狗粮, 我连夜做了这个程序!...
  12. 新一代视频编码标准:HEVC、AVS2和AV1性能对比报告
  13. 小程序转发功能(类似于美团红包转发功能)的简单实现
  14. macOS Big Sur 11.5 (20G71) 正式版 DMG、ISO、IPSW 百度网盘下载
  15. 竖流式沉淀池三角堰计算_一种辐流式沉淀池的双侧堰出水构造的制作方法
  16. 吴海燕C语言真题,基于APP的智能婴儿床系统的设计与实现开题报告
  17. OI模板 卢卡斯定理
  18. yolov5方框的颜色及粗细更改
  19. c语言 运算符的作用,C语言运算符
  20. 基于STM32,TB6612,TCRT5000的简易红外循迹小车

热门文章

  1. python3GUI--下载抖音小姐姐所有无水印视频(附源码)
  2. go fyne 跨平台开发框架 gui 编程 抖音下载 去水印编程 学习交流
  3. 教你快速打粤语正字 讯飞输入法语音输入1分钟400字
  4. 图片去水印工具(Teorex Inpaint) v7.1
  5. Oracle+ogg-00664,OGG采用NET8方式读取ASM中日志报OGG-00664(ORA-12162),配置如下:
  6. MLA与APA二种文件格式的文献引用写作技巧
  7. 大核注意力Large Kernel Attention(LKA)
  8. html转换为pdf c#,HTML转PDF(C# itextsharp)
  9. SpringBoot使用自定义https
  10. 元素周期表排列的规律_元素周期表的排列有什么规律?