一. 单例模式(Singleton
)
所谓单例模式,也就是说不管什么时候都要确保只有一个对象实例存在。很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。这些场景下,就非常适合使用单例模式。
总结起来,就是说不管初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。
用Python 实现单例模式的方法有很多,先来看第一种方式。
# !/usr/bin/python3
# -*- coding:utf-8 -*- class singleton(object):
"""
单列模式
""" class _A(object):
"""
真正干活的类,对外隐藏。
""" def __init__(self):
pass def display(self):
""" 返回当前实例的ID,是全局唯一的"""
return id(self) # 类变量,用于存储_A 的实例
_instance = None def __init__(self):
""" 先判断类变量中是否已经保存了_A 的实例,如果没有则创建一个后返回"""
if singleton._instance is None:
singleton._instance = singleton._A() def __getattr__(self, attr):
return getattr(self._instance, attr) if __name__ == "__main__":
# 创建2个实例
s1 = singleton()
s2 = singleton()
print(id(s1), s1.display())
print(id(s2), s2.display())
# !/usr/bin/python3 # -*- coding:utf-8 -*- class singleton(object): """ 单列模式 """ class _A(object): """ 真正干活的类,对外隐藏。 """ def __init__(self): pass def display(self): """ 返回当前实例的ID,是全局唯一的""" return id(self) # 类变量,用于存储_A 的实例 _instance = None def __init__(self): """ 先判断类变量中是否已经保存了_A 的实例,如果没有则创建一个后返回""" if singleton._instance is None: singleton._instance = singleton._A() def __getattr__(self, attr): return getattr(self._instance, attr) if __name__ == "__main__": # 创建2个实例 s1 = singleton() s2 = singleton() print(id(s1), s1.display()) print(id(s2), s2.display())
Output:
1917919737784 1917919737896
1917919738008 1917919737896
以上的单列模式实现中,借助了使用类变量 Singleton._instance 来存储创建的实例,并且保证只会创建一次实例。由于 Python 是一门动态语言,可以在运行时改变类定义。上面的代码中,在首次初始化 Singleton 时,将首次生成类 _A 的实例,并将其存储到 Singleton._instance 中,以后每次初始化 Singleton 时都从 Singleton._instance 获取真正干活的实例,这样就实现了单例模式。
从输出中可以看到,尽管创建了两个不同的实例(实例 ID 不同),但是访问其属性的时候得到的 ID 是一致的。以上代码虽然很好的实现了单例模式,但是在真正的项目开发中这种方式却不够灵活,因为要将真正干活的类内置在单例类中。 我们知道在 Python 中装饰器很好用,那能否将单例类实现成一个装饰器呢?答案是肯定的,下面实现代码。
# !/usr/bin/python3
# -*- coding:utf-8 -*-
import sqlite3
from flask import current_app
from flask import _app_ctx_stack as stack class SQLite3(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app) def init_app(self, app):
"""典型的 Flask 扩展的初始化方式"""
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
app.teardown_appcontext(self.teardown) def connect(self):
"""连接到 sqlite 数据库"""
return sqlite3.connect(current_app.config['SQLITE3_DATABASE']) def teardown(self, exception):
"""关闭 sqlite 链接"""
ctx = stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close() @property
def connection(self):
"""
单例模式在这里:使用 flask._app_ctx_stack 存放 sqlite 链接,
每次获取数据库链接时都通过 connection 获取
"""
ctx = stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
return ctx.sqlite3_db
在以上的代码中,在每次使用数据库的时候通过 SQLite3.connection 获取数据库连接就可以了。SQLite3.connection保证了数据库连接只会发生一次,其原理和之前实现单例模式的方式相同,只不过这里存储实例的地方变成 flask._app_ctx_stack了。
通过以上几段代码,可以看到单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。