【python设计模式-创建型】单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例:
- 1、一个班级只有一个班主任。
- 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
- 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
实现
1、第一种方式
自己定义一个包,例如mySingleton.py
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
然后在其它的python文件中就可以使用:from mySingleton import singleton
2、第二种方式:使用装饰器
def Singleton(cls):
_instance={}
def _singleton(*args,**kargs):
if cls not in _instance:
_instance[cls]=cls(*args,**kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2) a2 = A(3) print(a1.a) print(a2.a) print(a1.x) print(a2.x) print(id(a1)) print(id(a2))
运行结果:
值得关注的几个点:
(1) a1和a2的内存地址是一致的,说明是同一个对象;
(2)单例模式只实例化一次类,因此a1.x和a2.x的值都是a1初始化之后的值,也就是2。
3、使用类
class Singleton(object):
def __init__(self,x):
self.x=x
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
a1 = Singleton(4)
a2 = Singleton(5)
print(id(a1))
print(id(a2))
print(a1.x)
print(a2.x)
print(id(a1.instance(2)))
print(id(a2.instance(3)))
print(a1.x)
print(a2.x)
输出:
需要关注的几个点:
(1)Singleton的实例a1和a2不是同一个对象,但是a1.instance()和a2.instance()确是同一个对象。
(2)x初始化之后的值没有被instance(x)所改变。
也就是这里的单例实际上是Singleton.instance()
但是上述这种实现会存在线程不安全,例如以下代码:
class Singleton(object):
def __init__(self):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
import threading
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
看起来也没有问题,那是因为执行速度过快,如果在init方法中有一些IO操作,就会发现问题了,下面我们通过time.sleep模拟
我们在上面__init__方法中加入以下代码:
def __init__(self):
import time
time.sleep(1)
问题出现了!按照以上方式创建的单例,无法支持多线程
解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全
import time
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行:
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
这样,一个可以支持多线程的单例模式就完成了。
4、基于__new__方法实现(推荐使用,方便)
通过上面例子,我们可以知道,当我们实现单例时,为了保证线程安全需要在内部加入锁
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()
5、基于metaclass实现
"""
1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)
2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
"""
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
pass
obj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
obj() # 执行Foo的 __call__ 方法
元类的使用:
class SingletonType(type):
def __init__(self,*args,**kwargs):
super(SingletonType,self).__init__(*args,**kwargs)
def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
print('cls',cls)
obj = cls.__new__(cls,*args, **kwargs)
cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
return obj
class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
obj = Foo('xx')
实现单例模式:
import threading
class SingletonType(type):
_instance_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with SingletonType._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance
class Foo(metaclass=SingletonType):
def __init__(self,name):
self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
结果:
参考:
https://www.runoob.com/design-pattern/singleton-pattern.html
https://www.cnblogs.com/chillax1314/articles/8287333.html
https://my.oschina.net/u/4051725/blog/4379773
https://www.cnblogs.com/ExMan/p/10426937.html
- 关于oracle invalid components问题的解决(28天)
- 11g升级性能问题之一 重建user_synonyms (笔记27天)
- 数据库shutdown之后无法启动的问题 (46天)
- 关于oracle里的process总结(45天)
- 通过top命令抓取cpu高消耗的sql (44天)
- 关于字符串匹配查找的总结(43天)
- 一条sql语句导致的数据库宕机问题及分析(42天)
- 外部表的导入导出问题 (41天)
- 当我们和计算机交互时,它看到的是什么?
- 一条sql语句“导致”的数据库宕机问题及分析 (38天)
- rman数据备份恢复学习笔记(49天)
- 虚拟专用数据库VPD应用 (48天)
- 关于创建视图的问题(48天)
- 性能调优之redo切换频率(47天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用ng-container标签在SAP Spartacus里插入UI
- 自定义SAP Spartacus Cart界面
- 还是只使用console.log()进行调试?好吧,其实还有更多。
- SNMP++: Transport is not supported
- Codeforces Round #666 (Div. 2) A-D
- 深度剖析前端JavaScript中的原型(JS的对象原型)
- dotnet OpenXML 颜色变换
- n维空间的多面体的有向测度和重心
- 只会Vue怎么开发小程序?Vue和微信小程序的到底有哪些区别?
- VBA CreateObject函数如何找到所需要的依赖文件
- dotnet OpenXML 如何获取 schemeClr 颜色
- API——注册表读取
- VBA操作注册表
- VBA快速提取引用工程的代码
- NuGet 如何设置图标