首页 > 代码库 > Python版设计模式: 创建型模式:单例模式和工厂模式家族

Python版设计模式: 创建型模式:单例模式和工厂模式家族

一. 单例模式(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())
View Code

# !/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
View Code

 

在以上的代码中,在每次使用数据库的时候通过 SQLite3.connection 获取数据库连接就可以了。SQLite3.connection保证了数据库连接只会发生一次,其原理和之前实现单例模式的方式相同,只不过这里存储实例的地方变成 flask._app_ctx_stack了。

通过以上几段代码,可以看到单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。

Python版设计模式: 创建型模式:单例模式和工厂模式家族