@rfish
2015-07-18T13:05:45.000000Z
字数 3341
阅读 1667
实验楼
确保只有一个对象实例存在,所有的信息都从这个对象获取
单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。
_A不是Singleton
# -*- coding: utf-8 -*-class Singleton(object):"""单例模式"""class _A(object):"""真正干活的类, 对外隐藏"""def __init__(self):passdef display(self):""" 返回当前实例的 ID,是全局唯一的"""return id(self)# 类变量,用于存储 _A 的实例_instance = Nonedef __init__(self):""" 先判断类变量中是否已经保存了 _A 的实例,如果没有则创建一个后返回"""if Singleton._instance is None:Singleton._instance = Singleton._A()def __getattr__(self, attr):""" 所有的属性都应该直接从 Singleton._instance 获取"""return getattr(self._instance, attr)if __name__ == '__main__':# 创建两个实例s1 = Singleton()s2 = Singleton()print(id(s1), s1.display())print(id(s2), s2.display())
类变量与实例变量:
类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象解释
在上面的代码中由于
内置类是在__init__中实例化,而只有当类变量_instance为None时才会实例化。而只有第一次执行时,才为None(因为_instance是类变量即所有对象共享,在第二次实例类的时候,是不为None的)所以类_A是永远只有一个实例的。
# -*- coding: utf-8 -*-class Singleton:"""单例类装饰器,可以用于想实现单例的任何类。注意,不能用于多线程环境。"""def __init__(self, cls):""" 需要的参数是一个类 """self._cls = clsdef Instance(self):"""返回真正的实例"""try:return self._instanceexcept AttributeError:self._instance = self._cls()return self._instancedef __call__(self):raise TypeError('Singletons must be accessed through `Instance()`.')def __instancecheck__(self, inst):return isinstance(inst, self._decorated)# 装饰器@Singletonclass A:"""一个需要单列模式的类"""def __init__(self):passdef display(self):return id(self)if __name__ == '__main__':s1 = A.Instance()s2 = A.Instance()print(s1, s1.display())print(s2, s2.display())print(s1 is s2)
- 装饰器
- 看不懂
# -*- coding: utf-8 -*-import sqlite3from flask import current_appfrom flask import _app_ctx_stack as stackclass SQLite3(object):def __init__(self, app=None):self.app = appif 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.topif hasattr(ctx, 'sqlite3_db'):ctx.sqlite3_db.close()@propertydef connection(self):""" 单例模式在这里:使用 flask._app_ctx_stack 存放 sqlite 链接,每次获取数据库链接时都通过 connection 获取"""ctx = stack.top# 判断 ctx 中是否已经包含了sqlite 链接if ctx is not None:if not hasattr(ctx, 'sqlite3_db'):# 如果没有的话,才创建, 这一步操作只会发生一次ctx.sqlite3_db = self.connect()return ctx.sqlite3_db
一个函数,传入需要创建的产品类型,然后返回相应的产品就行了。(但是只能返回一个产品)
在创建
父类工厂抽象类中,定义多个方法。在后面的工厂类中继承该类,并重写方法,就可以返回两个以及以上的产品
问题1:
在学习单例模式时,我们实现了两种单例模式,并且还看到了单例模式在 Flask 扩展中的使用。但是似乎我们实现的单例模式不能再多线程环境中工作?你能用 Python实现一个能在多线程环境中正常工作的单例模式吗?并给出测试代码。
解答:
单例模式看了实验手册还是不怎么明白,上网查询后看到多线程使用加锁-双重锁定有两个多线程的线程单例模式:饿汉式单例类 懒汉式单例类
实例代码:
#encoding=utf-8#单例模式def PrintInfo(info):print info.decode('utf-8').encode('utf-8')import threading#单例类class Singleton():instance=Nonemutex=threading.Lock()def _init__(self):pass@staticmethoddef GetInstance():if(Singleton.instance==None):Singleton.mutex.acquire()if(Singleton.instance==None):PrintInfo('初始化实例')Singleton.instance=Singleton()else:PrintInfo('单例已经实例化')Singleton.mutex.release()else:PrintInfo('单例已经实例化')return Singleton.instancedef clientUI():Singleton.GetInstance()Singleton.GetInstance()Singleton.GetInstance()returnif __name__=='__main__':clientUI();
问题2:
虽然抽象工厂模式解决了工厂方法模式中遇到的问题,但是抽象工厂模式也是有缺陷的,你能总结下抽象工厂优缺点吗?
解答:
优点:
1.增加新的产品的时候很方便,只需要增加对应的代码,而不需要修改原有代码。
2.从抽象的角度去看,使用的时候,永远只需要从一个入口去取我们需要的产品。
缺点:
每次修改代码的时候,还是需要修改两个地方,一个是父类抽象工厂,需要增加对应的接口,然后是在继承父类的类中复写对应的接口。