Python 下面最简单的单例模式写法

二十几种设计模式中,单例模式是最简单最常用的一种。在其他语言里面实现单例模式要写不少代码,但是在 Python 里面,有一种非常简单的单例模式写法。

为了演示这种简单的写法,我们首先创建一个文件,DBUtil.py文件,用来模拟数据库操作类。这个文件里面的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DBUtil:
def __init__(self):
self.conn = self.connect()

def connect(self):
print('创建数据库连接')
return 'connect'

def write(self, data):
print(f'写入数据:{data}')

def read(self):
print('从数据库中读取数据')
return 123

现在我们创建两个文件:a.pyb.py,用来模拟在一个工程里面的两个不同地方同时调用数据库操作类并初始化的过程。

a.py内容如下:

1
2
3
4
5
6
from DBUtil import DBUtil
from b import run

data = run()
db_util = DBUtil()
db_util.write(data)

b.py的内容如下:

1
2
3
4
5
6
from DBUtil import DBUtil

def run():
db_util = DBUtil()
data = db_util.read()
return data

运行效果如下图所示:

可以看到,创建数据库连接被打印了两次,说明DBUtil类被实例化了两次。对应到真实的项目中,就是创建了多个到数据库的链接。这样是很浪费资源的。

当然,你可以在 a.py中初始化DBUtil,然后把这个对象作为参数传入run函数里面,再run函数里面调用这个对象的read()方法。

但是在实际项目中,往往会出现很多层的调用,如果要把一个对象一层一层传下去,不仅让参数列表显得杂乱,还容易漏掉或者搞错顺序。

所以,使用单例模式就能避免通过参数传递对象,但又不会创建多个数据库连接。

网上关于单例模式的代码有很多。本文将会介绍最简单的一种,利用 Python 的import机制。在 Python 里面,一个模块只会被导入1次,如果多次使用import xxx导入同一个模块,后面的导入语句会被自动忽略。你用这个机制,我们就能很容易实现单例模式。

修改DBUtil.py,在它的最下面加上一行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DBUtil:
def __init__(self):
self.conn = self.connect()

def connect(self):
print('创建数据库连接')
return 'connect'

def write(self, data):
print(f'写入数据:{data}')

def read(self):
print('从数据库中读取数据')
return 123

db_util = DBUtil()

修改a.py:

1
2
3
4
5
from DBUtil import db_util
from b import run

data = run()
db_util.write(data)

修改b.py:

1
2
3
4
5
from DBUtil import db_util

def run():
data = db_util.read()
return data

运行以后的效果如下图所示:

可以看到,创建数据库连接只打印了1次,说明单例模式成功。

这种单例模式非常简单,但是有一个弊端,就是无法实现懒加载。程序刚刚开始运行,DBUtil类就会被实例化,无法做到等到需要的时候才实例化。