Python中的单例模式的集中实现方式及优化

单例模式

单例模式(Singleton Pattern)是一种常见的软件设计模式,主要目的是为了确保某一个类只有一个示例的存在。

例:某个服务器程序的配置信息存放在一个文件夹中,客户端通过一个AppConfig的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建AppConfig对象的实例,这样会导致系统中存在多个AppConfig的实例对象,严重浪费内存

实现单例模式的集中方法

1、使用模块

其实,Python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件。
我们只需要在一个模块中定义函数和数据,就可以获得一个单例对象

1
2
3
4
class SingLeton:
def foo(self):
pass
singleton = SingLeton()

保存代码到文件mysingleton.py中。

1
from mysingleton import singleton

2、使用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def sing_leton(cls):
_instance = {}

def singleton(*args,**kwargs):
if cls not in _instance:
_instance[cls] = cls(*args,**kwargs)
return singleton

@sing_leton
class A:
a = 1

def __init__(self,x=0):
self.x = x

a1 = A(1)
a2 = A(2)

3、使用类

1
2
3
4
5
6
7
8
9
class sing_leton:
def __init__(self):
pass

@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance

当然一般情况下,大家以为这样就可以完成单例模式了,但是这样在使用多线程的时候会产生线程不安全

1
2
3
4
5
6
7
8
import threading
def task(arg):
obj = sing_leton.get_instance()
print(obj)

for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()

程序执行↓

1
2
3
4
5
6
7
8
9
10
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>
<__main__.sing_leton object at 0x000001F80176FAC8>

看起来好像也没有什么毛病,但是真的没有毛病吗?,我们来修改一下相关的代码

1
2
3
4
5
6
7
8
9
10
class sing_leton:
def __init__(self):
import time
time.sleep(1)

@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance

执行得到了这样的结果。

1
2
3
4
5
<__main__.sing_leton object at 0x000001ED128C4438><__main__.sing_leton object at 0x000001ED128C4F60>
<__main__.sing_leton object at 0x000001ED128C4898><__main__.sing_leton object at 0x000001ED128C45C0>
<__main__.sing_leton object at 0x000001ED128C4F28><__main__.sing_leton object at 0x000001ED128C46D8>
<__main__.sing_leton object at 0x000001ED128C4588><__main__.sing_leton object at 0x000001ED1290EC50>
<__main__.sing_leton object at 0x000001ED128C4550><__main__.sing_leton object at 0x000001ED1290EA58>

那么问题出现了,创建的单例模式,却无法支持多线程的访问。那有什么解决方案呢。

解决方法: 加锁啊!加锁的部分串行,不加锁部分并行。保证了安全。

1
2
3
4
5
6
7
8
9
10
11
12
class sing_leton:
_instance_lock = threading.Lock()
def __init__(self):
import time
time.sleep(2)

@classmethod
def get_instance(cls,*args,**kwargs):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance

执行如下:

1
2
3
4
5
6
7
8
9
10
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>
<__main__.sing_leton object at 0x000001ED128A9470>

但是这里还有一个小的问题,就是当我们下一次进行性实例化对象的时候,已经史丹利模式了,但是我们还是加了锁,这样不太好,在进行一些优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
class sing_leton:
_instance_lock = threading.Lock()
def __init__(self):
import time
time.sleep(2)

@classmethod
def get_instance(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance

这种单例模式会有限制,以后实例化必须通过 instance = sing_leton.get_instance()

4、基于new方法实现(推荐)

当我们实例化一个对象的时,先执行类的__new__方法(没有定义__new__的时候,默认object.__new__),实例化对象;然后执行类的init方法,对这个对象进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
class sing_leton:
_instance_lock = threading.Lock()

def __init__(self):
pass

def __new__(cls,*args,**kwargs):
if not hasattr(sing_leton,"_instance"):
with sing_leton._instance_lock:
if not hasattr(sing_leton,"_instance"):
sing_leton._instance = sing_leton(*args,**kwargs)
return sing_leton._instance

如果是采用这种方法的话,实例化对象的时候和平时就一样了。instance = sing_leton()

5、基于metaclass方式实现

1
2
1、类由type创建,创建类时,type的__init__方法自动执行,类()执行type的__call__方法(类的__new__方法,类的__init__方法)
2、对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法
1
2
3
4
5
6
7
8
9
10
11
12
class sing_leton_Type(type):
_instance_lock = threading.Lock()
def __call__(cls,*args,**kwargs):
if not hasattr(cls,"_instance"):
with sing_leton_Type._instance_lock:
if not hasattr(cls,"_instance"):
cls._instance = super(sing_leton_Type,cls).__call__(*args,**kwargs)
return cls._instance

class Foo(metaclass=sing_leton_Type):
def __init__(self,name):
self.naem = name

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:https://blog.suixin.kim/2018/05/01/python-singleton-pattern/