单例模式是一种创建型模式,它的核心要求是一个类只有一个实例对象,这个要求是违反直觉的, 因为在学习面向对象时,所有教程都告诉你,一个类可以创建出多个实例对象。
之所以提这样的问题,是因为在实践中,需要对一些资源的访问做限制,比如数据库连接。
假设你实现了一个专门负责连接 mysql
进行操作的类,它维护了一个大小为10的连接池。
在程序的其他地方,如果允许这个类创建出多个实例对象,那么每创建出一个实例对象,
都要维护一个大小为10的连接池,那么你的程序与 mysql
之间所建立的连接就会越来越多,
这显然会导致异常灾难。
在单例模式下,一个类只能创建出一个实例,不管你怎样使用类的构造函数,都永远只有一个实例, 这样就能对那些敏感的资源做出了访问限制。
Python实现单例模式
Python 有很多种实现单例模式的方法,有简单的,有复杂的,先来看看最简单的实现方式。
利用模块实现
编写脚本 single.py
:
with open('xx_single.py', 'w') as fo:
fo.write('''
class DbSingleton():
def __init__(self, host='localhost' , port=8008, username='jovyan', password='123456'):
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 xx_single import db_singleton
from threading import RLock
创建cls的对象时候调用
class SingletonType(type):
single_lock = RLock()
def __call__(cls, *args, **kwargs):
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)
True
在第一次创建Singleton的实例对象时, Singleton
还没有 _instance
属性,待实例对象被创建好以后,
将这个唯一的示例对象赋值给 cls._instance
,其后再创建新的示例对象时,
Singleton
已经有了 _instance
属性,便不会创建新的实例对象了。
使用元类来实现单例模式,元类是专门用来创建自定义类的类,就上面这段代码而言,
类 Singleton
是 SingletonType
的实例。
当语句 Singleton('第1次创建')
被执行时,则是在调用执行 SingletonType
的 __call__
方法,
在这里做一些手脚,就能确保永远只有一个 Singleton
实例对象被创建。
这个单例模式中,还考虑到了多线程条件下单例模式的应用问题,在创建实例对象时特地加了锁,这样更加万无一失。
除了利用元类,还可以利用装饰器,具体方法可以参考的另一篇文章python实现单例模式的5种方法。