单例模式是一种创建型模式,它的核心要求是一个类只有一个实例对象,这个要求是违反直觉的,因为在学习面向对象时,所有教程都告诉你,一个类可以创建出多个实例对象。
之所以提这样的要求,是因为在实践中,需要对一些资源的访问做限制,比如数据库连接。假设你实现了一个专门负责连接mysql进行操作的类,它维护了一个大小为10的连接池。在程序的其他地方,如果允许这个类创建出多个实例对象,那么每创建出一个实例对象,都要维护一个大小为10的连接池,那么你的程序与mysql之间所建立的连接就会越来越多,这显然会导致异常灾难。
在单例模式一下,一个类只能创建出一个实例,不管你怎样使用类的构造函数,都永远只有一个实例,这样就能对那些敏感的资源做访问限制。
python 有很多种实现单例模式的方法,有简单的,有复杂的,我们先来看看最简单的实现方式
编写脚本single.py
class DbSingleton():
def __init__(self, host, port, username, password):
self.host = host
self.port = port
self.username = username
self.password = password
self.pool = None # 连接池
def connect(self):
print("建立连接")
db_singleton = DbSingleton()
在single.py 脚本中,我实现了一个非常普通的类DbSingleton, 并在脚本里创建实例对象db_singleton, 在其他的模块里,如果需要使用数据库连接,只需要将db_singleton 导入即可
from single import db_singleton
在任何模块里,不论这个导入的过程被执行了对少次,都永远只有一个db_singleton, 这就是python中实现单例模式的最简单的方法,它简单到让你质疑:如果直接使用DbSingleton 创建一个新的对象,不就等于是破坏了单例模式了么?
没错,如果你想破坏单例模式,总能够想到办法,使用设计模式的初衷一部分是为了防止开发人员犯错,但更重要的是让系统有良好的架构,便于维护和升级,你硬是要想办法破坏一种设计模式,那你总能找到办法。
既然受到质疑,那么我们就实现一个不容易破坏的单例模式
from threading import RLock
class SingletonType(type):
single_lock = RLock()
def __call__(cls, *args, **kwargs): # 创建cls的对象时候调用
with SingletonType.single_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) # 创建cls的对象
return cls._instance
class Singleton(metaclass=SingletonType):
def __init__(self, name):
self.name = name
single_1 = Singleton('第1次创建')
single_2 = Singleton('第2次创建')
print(single_1.name, single_2.name) # 第1次创建 第1次创建
print(single_1 is single_2)
在第一次创建Singleton的实例对象时,Singleton 还没有_instance属性,待实例对象被创建好以后,将这个唯一的示例对象赋值给cls._instance,其后再创建新的示例对象时,Singleton 已经有了_instance 属性,便不会创建新的实例对象了。
我使用元类来实现单例模式,元类是专门用来创建我们自定义类的类,就上面这段代码而言,类Singleton 是 SingletonType 的实例。
当语句 Singleton('第1次创建') 被执行时,则是在调用执行SingletonType 的 __call__方法,我在这里做一些手脚,就能确保永远只有一个Singleton 实例对象被创建。
这个单例模式中,我还考虑到了多线程条件下单例模式的应用问题,在创建实例对象时特地加了锁,这样更加万无一失。
除了利用元类,还可以利用装饰器,具体方法可以参考的我另一篇文章python实现单例模式的5种方法
QQ交流群: 211426309