Python 单例模式的几种实现方式
单例模式的几种实现方式
先来看几个魔法方法的简单运用:__new__, __init__, __call__。
class A(object):
def __init__(self, x):
print('x in __init__', x)
def __new__(cls, y): # 它只取下 cls 参数,并把其他参数传给 __init__
print('y in __new__', y)
return super(A, cls).__new__(cls)
def __call__(self, z): # 允许一个类的实例像函数一样被调用
print('z in __call__', z)
A('123')('abc') # 相当于先 a = A('123'), 再 a('abc')
# y in __new__ 123
# x in __init__ 123
# z in __call__ abc
1._new_(cls, *args, **kw) 方法实现
__new__ 构造方法至少需要一个 cls 参数,实例化时,解释器会自动填入;
需要注意的是,new 方法中调用 new 方法时不要再调用自己的 new 方法,会报【超出最大递归深度】错误;要调用父类的 new 方法(默认调用)。
class Singleton(object):
_instance = None # 新建一个类属性:需要使用类名前缀来访问
def __new__(cls, *args, **kw): # 创建实例时,判断是否已经存在,存在,返回,不存在,创建
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kw) # 不要调用自己的new方法, 即: cls.__new__(), 会无限递归的;
return cls._instance # 返回实例对象
def __init__(self):
pass
sing1 = Singleton()
sing2 = Singleton()
sing1 is sing2 # true
"""
若 _instance = {}, 则
if cls not in cls._instance:
cls._instance[cls] = object.__new__(cls, *args, **kw)
"""
2.函数装饰器实现:实例化前先调用这个装饰器返回的 inner() 函数,再返回实例
用不可变的 类地址 作为键,其 实例 作为值,每次创造实例时,首先判断该类是否存在实例,存在,直接返回该实例即可,否则新建一个实例并存放在字典中。
def singleton2(cls):
_instance = {} # 新建空字典; 不要_instance = None, 否则 inner 函数中赋值时,_instance 只会在函数作用域搜索,判断时会报错;
def inner(): # 判断是否已有实例,如无,则新建一个实例并返回
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls] # 返回的是实例对象
return inner
@singleton2
class Cls():
def __init__(self):
pass # 可以照常添加属性和方法
cls1 = Cls()
cls2 = Cls()
cls1 is cls2 # True
3.类装饰器实现:类装饰器可以直接依靠类内部的__call__方法来实现
Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及类实例对象。
__call__ 方法可以使类实例对象可以像调用普通函数那样,以 “对象名()” 的形式使用;此方法会在实例作为一个函数被 “调用” 时被调用;
如果定义了此方法,则 x(arg1, arg2, ...) 就相当于 x.__call__(arg1, arg2, ...) 的快捷方式。只要实现了__call__方法的对象都是可被调用对象。
class Singleton3(object): # 自定义类装饰器
def __init__(self, cls): # 赋初值
self.cls = cls
self.instance = {}
def __call__(self): # 调用类装饰器时, 判断是否已存在
if self.cls not in self.instance:
self.instance[self.cls] = self.cls()
return self.instance[self.cls] # 返回实例对象
@Singleton3
class Cls2(object):
def __init__(self):
pass
cls3 = Cls2()
cls4 = Cls2()
cls3 is cls4 # True
class Cls3():
pass
Cls33 = Singleton3(Cls3) # Cls3 传给 __init__ 了;
cls5 = Cls33() # “名称()” 可以理解为是 “名称.__call__()” 的简写;
cls6 = Cls33()
cls5 is cls6 # True
4.metaless 元类实现
元类->类->实例,简单来说,就是类是由元类创建的,type 是内置的元类,也可以自定义元类。
type(name, bases, dict):name 是类的名字,bases 是要继承的父类集合,dict 是这个类的属性。下面两条语句会创建相同的 type 对象:
class X: a = 1
X = type('X', (object,), dict(a=1)) # 动态的创建类对象,dict(a=1) 也可以直接用 {'a':1, 'func_name': func_name}
class Singleton4(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton4, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Cls4(metaclass=Singleton4):
pass
cls1 = Cls4()
cls2 = Cls4()
cls1 is cls2 # True
5.import 实现
python 模块是天然的单例模式:需要使用实例的时候,在文件中导入即可使用,从而减少相关实例的创建;
模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码;
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:
# my_singleton.py
class My_Singleton(object):
def foo(self):
print('My_Singleton')
mysingle = My_Singleton()
# other.py
from my_singleton import mysingle
mysingle.foo()
6.共享属性
创建实例时, 把所有实例的 __dict__ 都指向同一个字典,这样它们就会共享同一个属性,具有相同的属性和方法。
要查看对象中存储的所有值,可检查其 __dict__ 属性。如果要确定对象是由什么组成的,应研究模块 inspect。
注意:此时只是对象值相同,多个对象共享一个数据,依旧以最新的值为准,但不是同一个对象。
class Borg(object):
_state = {} # 共享属性字典
def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state # 指向同一个字典
return ob
class MyClass2(Borg):
a = 1
应用场景简介
每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;
当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。
模块 logger 就是一个单例模式;Windows的资源管理器是一个单例模式;线程池,数据库连接池等资源池一般也用单例模式;网站计数器;游戏场景管理器;
游戏中需要有 “场景管理器” 这样一种东西,用来管理游戏场景的切换、资源载入、网络连接等等任务。
这个管理器需要有多种方法和属性,在代码中很多地方会被调用,且被调用的必须是同一个管理器,否则既容易产生冲突,也会浪费内存资源。
某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,
也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。
Python 单例模式的几种实现方式的更多相关文章
- python 单例模式的四种创建方式
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- JAVA中单例模式的几种实现方式
1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...
- python中的三种输入方式
python中的三种输入方式 python2.X python2.x中以下三个函数都支持: raw_input() input() sys.stdin.readline() raw_input( )将 ...
- Python中的单例模式的几种实现方式的优缺点及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Python中的单例模式的几种实现方式的及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- python单例模式的几种实现方法
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Python基础:Python运行的两种基本方式
完成Python的安装之后,我们可以开始编写Python代码以及运行Python程序了.我们来看一下运行Python具体有哪几种方式 1.REPL 所谓REPL即read.eva.print.loop ...
- Python 45 css三种引入方式以及优先级
一:css三种引入方式 三种方式为:行间式 | 内联式 | 外联式 行间式 1.在标签头部的style属性内 2.属性值满足的是css语法 3.属性值用key:value形式赋值,value具 ...
- CSIC_716_20191129【 单例模式 的五种实现方式】
单例模式 单例模式:在确定类中的属性和方法不变时,需要反复调用该类的情况. 让所有通过该类实例化出的对象,都指向同一个内存地址. 优点:节省内存空间. 单例模式有五种表现形式: 1.通过class ...
随机推荐
- 搭建服务器之www-向外提供视频服务by html5 video标签
搭建好www服务器,主要目的有两个一个是试验下,另一个是想给女朋友个惊喜,给她个带视频的网页,嘿嘿当前测试下相应功能. 1,采用html5的视频功能:bideo标签. 源码如下: <!docty ...
- default和delete
在C++中,有四类特殊的成员函数,分别为:默认构造函数,默认析构函数,默认拷贝构造函数,默认赋值运算符.他们的作用为创建.初始化.销毁.拷贝对象. 虽然在类A中什么都没有定义,但是编译会通得过,因为编 ...
- undo和redo的区别
undo和redo的区别: undo一般用于事务的取消与回滚,记录的是数据修改前的值: redo一般用于恢复已确认但未写入数据库的数据,记录的是数据修改后的值.
- Vue3源码分析之 Ref 与 ReactiveEffect
Vue3中的响应式实现原理 完整 js版本简易源码 在最底部 ref 与 reactive 是Vue3中的两个定义响应式对象的API,其中reactive是通过 Proxy 来实现的,它返回对象的响应 ...
- vue之keep-alive的使用
keep-alive:是vue内置的一个组件,可以使被包含的组件保留状态或避免重新渲染.有两个生命周期函数:activated.deachtivated.在vue 2.1.0版本后新增了两个属性:in ...
- spring 整合shiro框架 模拟登录控制器。
一.导入shiro jar包. 我在maven项目中,将常用的jar包都放在里面. <?xml version="1.0" encoding="UTF-8&qu ...
- 如何使用 C++ 和 OpenCV 实现截屏
前言 实现屏幕截屏需要用到 Windows API,所以需要包括 Windows.h 头文件.同时我们想要对截图做进一步的处理,就需要用到 OpenCV.关于 OpenCV 的安装与编译可以参见 &l ...
- https的页面内嵌入http页面报错的问题
1.https的页面内嵌入http页面报错 在HTTPS的页面上嵌入http的页面时,浏览器会直接报错.比如在HTTPS页面上用 iframe 直接嵌入一个 http 页面,比如我们可以在百度上直接嵌 ...
- 密码学之PRP/PRF转换引理
本文将介绍密码学中的PRF.PRP等相关概念,并介绍 PRP/PRF 转换引理及其证明,希望读完本文后,你能对现代密码学中这几个基础概念有所了解. 在开始本文前,希望你有如下预备知识: 现代密码学是怎 ...
- centOS 强制卸载PHP
centOS上的php过低是需要重新安装时,不得不卸载自定义安装,如下操作 查看php版本命令: #php -v 这个命令是删除不干净的 #yum remove php 因为使用这个命令以后再用 #p ...