python设计方案_【Python】单例模式的五种设计方案

单例模式是一种常用的开发设计模式,它的主要目的就是确保只有一个实例对象的存在,也就是说当你希望一个类的功能比较单一,你只需要一个实例对象就可以完成需要的时候,就可以使用单例模式,以此来节省内存资源。

比如我们在web开发项目中,我们经常需要做到的一个功能就是使用单例模式来开发短信验证码这个功能。我们通常是使用通讯产品来发送短信验证码,那么我们就只需要用一个实例对象去完成这个短信发送的功能就可以了。

1. 模块实现单例模式

大家应该都知道,模块的导入只能够让被导入程序执行一次,你多次导入也只会执行一次,那么我们可以说模块就是一个天然的单例模式,因此,我们只需把相关函数和数据定义在一个模块中,使用的时候将它导入到其他模块使用就可以达到单例模式的效果了。

single.py

···

class Singleton(object):

def foo(self):

pass

print(1)

singleton = Singleton()

from singleton import singleton

from singleton import singleton

执行结果:

1

2. 使用装饰器实现单例模式

装饰器的作用相信大家都是知道的,可以给我们别的函数或者类添加一个功能,那么我们同样的可以给类写一个逻辑,让类只能生成一个实例对象。

def Singleton(cls):

_instance = {}

def singleton(*args, **kargs):

if cls not in _instance:

_instance[cls] = cls(*args, **kargs)

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(a1)

print(a2)

执行结果:

3. 使用类方法实现单例模式

当我们实用类直接创建实例对象的时候,创建的并不是单例对象,那么我们需要在类中定义一个类方法来编辑逻辑实现单例模式,主要思路就是会去判断类是否有_instance这个属性,如果有则直接返回这个实例,没有则创建实例。

class Singleton(object):

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

pass

@classmethod

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

if not hasattr(Singleton, '_instance'):

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

return Singleton._instance

s1 = Singleton.get_instance()

s2 = Singleton.get_instance()

print(s1)

print(s2)

执行结果:

但是这样去实现单例模式有个隐患,当我们加入多线程去创建实例对象的时候,我们的执行速度够快还不会出现影响,当执行速度不够快的时候,一个线程去创建实例然后拿到了_instance这个属性去判断,其他的线程可能也同时会拿到这个_instance这个属性,发现并没有实例存在,所以这两个线程就会同时创建一个实例,就会造成创建了多个实例的现象,我们的单例模式自然也就失效了。举例来说明,我们这里直接加上这个休息时间演示执行慢的情况。

import threading

import time

class Singleton(object):

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

time.sleep(1)

@classmethod

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

if not hasattr(Singleton, '_instance'):

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

return Singleton._instance

def task(arg):

obj = Singleton.get_instance(arg)

print(obj)

for i in range(10):

t = threading.Thread(target=task, args=[i, ])

t.start()

执行结果:

现在我们是不是就发现了这个实例对象变为了不同的ip了?这个也就是我们刚才所说道的这个原因。那么我们怎么去解决这问题呢?在这里我们可以通过加上线程锁达到效果。我们加了线程锁之后,给这个_instance加上这个锁,让每一个县城用完了之后才释放它,接着下一个线程才能拿到它继续运行程序,就不会再造成同时拿到这个属性值而直接创建了不同的实例的情况。

import threading

import time

class Singleton(object):

_instance_lock = threading.Lock()

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

time.sleep(1)

@classmethod

def get_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.get_instance(arg)

print(obj)

for i in range(10):

t = threading.Thread(target=task, args=[i, ])

t.start()

执行结果:

4. 使用__new__方法实现单例模式

大家应该都知道我们的实例对象就是通过__new__方法创建出来的,如果我们重写类的__new__方法,我们是不是可以直接把这个可以创建多个实例的情况改写为只能创建一个实例对象呢?答案是肯定的,我们可以在重写__new__方法的时候,添加判断如果存在了实例对象,那么直接返回已经创建了的对象即可,这样就可以达到我们的这个效果了。

import threading

class Singleton(object):

_instance_lock = threading.Lock()

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

pass

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

if not hasattr(cls, '_instance'):

with Singleton._instance_lock:

if not hasattr(cls, '_instance'):

Singleton._instance = super().__new__(cls)

return Singleton._instance

def task(arg):

obj = Singleton()

print(obj)

for i in range(10):

t = threading.Thread(target=task, args=[i, ])

t.start()

运行结果:

None

None

None

None

None

None

None

None

None

5. 使用元类metaclass实现单例模式

Python的元类是控制类的创建的类,既然如此,那么我们是不是可以再在创建类的时候通过元类来创建我们的类,使得它只能生成一个实例对象呢?

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(id(obj1))

print(id(obj2))

执行结果:

好了,那么这里就是给大家介绍的几种创建单例模式的方法,总的来说,不管有没有用,面试的时候,我们装x还是有作用的,你get到了吗?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值