python 单例设计模式_python设计模式之单例模式

无论是在python代码中,还是面试中单例设计模式都是经常被问到和使用的,例如面试中会让你用代码实现单例模式分几种不同的方式,或者问你在平常工作中哪些地方有用到单例设计模式,然后深入探讨。

在本文中我将针对这两个问题来回答和用python代码来编写我们的单例模式。

首先,我们要了解什么是单例模式--官方解释是:确保一个类只有一个实例(也就是类的对象),并且提供一个全局的访问点(外部通过这个访问点来访问该类的唯一实例)。通俗的说就是,无论类实例化多少次,都只有一个相同的对象产生,并且可以通过一个具柄去访问这个这个唯一实例。

其次,我们平时代码中碰到是用单例模式设计的代码有哪些呢?

应用框架的配置文件config(如flask,django框架的配置文件)

线程池,数据库连接池的创建

应用程序的日志应用

这些应用都有一个共性那就是:他们都是全局共享的对象不会发生变化,不希望重复创建,这样有利于节省空间和减少性能消耗。

python实现单例模式的几种方式分别有:

一、类方法实现

classSingleType:

_instance=None

@classmethoddefinstance(cls):if notcls._instance:

cls._instance=SingleType()returncls._instancefor i in range(100):print(SingleType.instance())

看一下打印结果:

1490424-20200624111644741-330770459.png

返回的都是同一个对象,实现了我们说的,无论调用多少次,都只返回同一个实例,但是这在多线程的环境中,可能会存在返回多个实例,我们实验一下。

import threading

import time

class SingleType:

_instance = None

def __init__(self):

time.sleep(1)

super().__init__()

@classmethod

def instance(cls):

if not cls._instance:

cls._instance = SingleType()

return cls._instance

def task():

obj = SingleType.instance()

print(obj)

for i in range(10):

t = threading.Thread(target=task).start()

print(SingleType.instance())

这是多线程环境测试的代码结果如下:

1490424-20200624112330391-360848730.png

可以看到在多线程的环境中,我们产生了两个2个对象,这就违背了我们的单例原则了,那怎么解决它呢,答案是加锁。

importthreadingimporttimeclassSingleType:

_instance=None

_lock=threading.Lock()def __init__(self):

time.sleep(1)

super().__init__()

@classmethoddefinstance(cls):if notcls._instance:

with SingleType._lock:if notcls._instance:

cls._instance=SingleType()returncls._instancedeftask():

obj=SingleType.instance()print(obj)for i in range(10):

t= threading.Thread(target=task).start()

加锁后我们看运行结果:

1490424-20200624113803144-32371482.png

这样就成功解决在多线程环境下单例的问题了, 至于问什么在最外层还要加一个if判断,是为了提高程序的性能。

二、装饰器模式实现

这种实现方式在我上一片将装饰器一文中是有提及的,在这我们再实现一次

defdecorator(cls):def wrap(*args, **kwargs):if notcls._instance:

cls._instance=cls()returncls._instancereturnwrap

@decoratorclassSingleType:

_instance=Nonedef __init__(self):

super().__init__()for i in range(10):

obj=SingleType()print(obj)

当然这里也会出现多线程创建多个实例的问题,可以参照上一个例子自行去实现多线程的版本。

三、使用__new__方法实现

在说__new__方法实现之前,我们需要有个前提知识,我们对象在实例话时会调用__init__方法,但是在调用__init__方法之前,类会先调用__new__方法,__new__方法中决定实例化怎样的对象(可以是调用object的__new__方法正常接着实例话,可以生成其他类的实例等)具体知识可以自行去补充,本文主要讲单例模式。

classSingleType:

_instance=Nonedef __new__(cls, *args, **kwargs):if notcls._instance:

cls._instance= super().__new__(cls)returncls._instancedef __init__(self):

super().__init__()for i in range(10):

obj=SingleType()print(obj)

查看运行结果:

1490424-20200624115846456-1083243145.png

四、使用元类实现单例模式

前提知识: 实例化对象时方法调用顺序是: type类的__cal__方法,其中会调用__new__方法和__init__方法

importthreadingclassSingleType(type):

lock=threading.Lock()def __call__(cls, *args, **kwargs):if notcls._instance:

with SingleType.lock:if notcls._instance:

cls._instance= super().__call__(*args, **kwargs) # 这个地方也可以替换成 obj = cls.__new__(cls) --> cls.__init__(onj) --> cls._instance=objreturncls._instanceclass Test(metaclass=SingleType):

_instance=Nonefor i in range(10):

obj=Test()print(obj)

在代码注释部分的作用相当于自己实现type.__call__的功能。

至此python实现单例设计模式所有的实现方式已经讲完,既然说的是设计模式就要把设计模式的八大准则贴出来,时常警醒自己。

1、依赖倒置原则(DIP):高层模块不依赖于低层模块,而是都依赖于抽象;抽象不依赖于实现细节,实现细节应该依赖于抽象。

2、开放封闭原则(OCP):对扩展开放,对依赖关闭;类的模块是可扩展的,但是不可修改。

3、单一职责原则(SRP):一个类仅有一个能引起他的变化,变化的方向隐含着类的责任。

4、LisKov替换原则:派这类能够替换他的基类,继承表达了类的抽象。

5、接口隔离原则(ISP):不应该强迫客户程序依赖他们不用的方法,接口应该小而强大。

6、优先使用对象组合,而不是类继承:类的继承破坏了封装性,子类父类耦合度高,类继承通常称为“白箱复用和“,对象组合称为”黑箱复用”。并且对象组合只需要有良好的定义的接口,且耦合度低。

7、封装变化点:使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧修改,而不会对另一侧产生不良影响。

8、针对接口编程,而不是针对实现编程:减少系统中个部分的依赖关系,从而实现”高内聚,松耦合“的类型设计方案;不将变量类型声明为某个特定的具体类,而是声明为某个接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值