一. 单例模式( 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())
使用装饰器
装饰器维护一个字典对象instances,缓存了所有单例类,只要单例不存在则创建,已经存在直接返回该实例对象。
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class Foo(object):
pass
foo1 = Foo()
foo2 = Foo()
print foo1 is foo2 # True
使用基类
__new__ 是真正创建实例对象的方法,所以重写基类的 __new__ 方法,以此来保证创建对象的时候只生成一个实例
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
class Foo(Singleton):
pass
foo1 = Foo()
foo2 = Foo()
print foo1 is foo2 # True
使用元类
元类(参考: 深刻理解Python中的元类 )是用于创建类对象的类,类对象创建实例对象时需要一定会调用 __call__ 方法,因此在调用 __call__ 时候保证始终只创建一个实例即可, type 是python中的一个元类。
class Singleton(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance
class Foo(object):
__metaclass__ = Singleton
foo1 = Foo()
foo2 = Foo()
print foo1 is foo2 # True
# !/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
View Code
以上的单列模式实现中,借助了使用类变量 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 了。
通过以上几段代码,可以看到单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。