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())

使用装饰器

装饰器维护一个字典对象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 了。

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

时间: 2024-10-25 14:26:42

Python单例模式和工厂模式学习笔记的相关文章

PHP设计模式之工厂模式学习笔记

在大型系统中,许多代码依赖于少数几个关键类.需要更改这些类时,可能会出现困难.例如,假设您有一个从文件读取的 User 类.您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类.这时候,使用工厂模式会很方便. 工厂模式是一种类,它具有为您创建对象的某些方法.您可以使用工厂类创建对象,而不直接使用 new.这样,如果您想要更改所创建的对象类型,只需更改该工厂即可.使用该工厂的所有代码会自动更改. 示例1:显示工厂类的一个示列. 等式的服务器端包括两个部分:数据库和一组 P

PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式_php实例

毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 单例模式 当需要保证某个对象只能有一个实例的时候,单例模式非常有用.它把创建对象的控制权委托到一个单一的点上,任何时候应用程序都只会仅有一个实例存在.单例类不应该可以在类的外部进行实例化一个单例类应该具备以下几个要素. 必须拥有一个访问级别为 private 的构造函数,有效防止类被随意实例化. 必须拥有一个保存类的实例的静态变量. 必须拥有一个访问这个实例的公共的静态

PHP单例模式与工厂模式详解

一.单例模式又称为职责模式,它用来在程序中创建一个单一功能的访问点,通俗地说就是实例化出来的对象是唯一的. 所有的单例模式至少拥有以下三种公共元素: 1. 它们必须拥有一个构造函数,并且必须被标记为private 2. 它们拥有一个保存类的实例的静态成员变量 3. 它们拥有一个访问这个实例的公共的静态方法 单例类不能再其它类中直接实例化,只能被其自身实例化.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 单例模式实例 <?php class Single { private $n

Java设计模式之享元模式学习笔记

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象. Java中String的实现就是一个典型的享元模式应用,Java中的String存在字符串常量池中,Java会确保一个字符串常量在常量池中只有一个拷贝.数据库连接池也是一个比较电信的享元模式应用,可简单理解为先初始化一定数量的数据库连接,

javascript 模式设计之工厂模式学习心得_js面向对象

模式类型:工厂模式 模式说明:常用模式之一,用来动态创建对象 适用范围:在运行期间需要在一系列可互换的子类中进行选择的类 注意事项:接口的实现,从而使不同子类可以被同等的对待,恰当的使用工厂模式,但不要拘泥与形式,理解本质. 关键点:以 函数/类/子类 构建的选择器 本质:函数作为选择器的使用 一般使用形式: 作为独立的选择器存在: 复制代码 代码如下: function FactoryMode(index){ switch(index){ case "index1" : return

PHP设计模式之:原型模式学习笔记

原型模式其实和工厂模式比较类似,都是用来创建对象的,只不过与工厂模式的实现不同.原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象.这样就免去了类创建时重复的初始化操作了.原型模式适用于大对象的创建,因为在创建一个大对象时,需要很大的开销.如果每次都去new就会消耗很大,原型模式仅需从内存拷贝既可. 还是继续通过实例来向大家演示一下 <?php /**  * 抽象原型角色  */ interface Prototype {     public function clone

java设计模式之外观模式学习笔记_java

外观模式: 又称门面模式: 外观Facade为子系统的一组接口提供一个一致界面,使得这组子系统易于使用(通过引入一个新的外观角色降低原系统复杂度,同时降低客户类与子系统的耦合度). 图片来源: 设计模式: 可复用面向对象软件的基础. 实现 案例需求: 租房 有过自己找房租房经历的同学能够体会得到找房是件很痛苦的事, 不光要挨个小区跑而且还要跟(二)房东讨价还价. 于是后来学聪明了, 不再自己挨门挨户的磨嘴皮子, 而是直接找像链家.我爱我家这样的房屋中介, 他们手上握有一定的房源, 我们只需付给他

PHP 正则表达式匹配模式学习笔记

PHP中对于正则处理文本提供了两种方式,一种是PCRE方式(PCRE库是一个实现了与perl 5在语法和语义上略有差异(详见下文)的正则表达式模式匹配功能的函数集. 当前的实现对应于perl 5.005.):另一个是POSIX方式.   PCRE函数库中的函数使用的模式语法非常类似perl. 表达式必须用分隔符闭合, 比如一个正斜杠(/). 分隔符可以使任意非字母数字, 除反斜杠()和空字节之外的非空白ascii字符. 如果分隔符 在表达式中使用, 需要使用反斜线进行转义. 自php 4.0.4

php Prototype原型模式学习笔记

待解决的问题:我们能否减少new的使用,同时避免需要新增对象的时候,了解对象的类名. 思路:php5提供了克隆方法,我们可以新增一个对象,然后每次需要新增和她同类的对象,克隆他就可以了. 原型(Prototype)模式示例:  代码如下 复制代码 <?php //机兵类 class Marine {   //所属的用户ID   public $playerID   //构造函数,参数为用户的id   public function __construct($id)   {   $this->p