python 单例模式
Posted wx63186321c235c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 单例模式相关的知识,希望对你有一定的参考价值。
参考链接:
Python中的单例模式的几种实现方式的及优化 - 听风。
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
在 Python 中,我们可以用多种方法来实现单例模式
一、场景:
配置文件configuration.yml
DB:
host: 1
username: 12
password: 12
database: 14
databasetype: 15
port: 33
note: C
存储配置文件的类MyDbinfo
import yaml
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, r, encoding=utf-8)
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x[DB][host]
self.username=x[DB][username]
self.password=x[DB][password]
self.database=x[DB][database]
self.port=x[DB][port]
self.note = x[DB][note]
现在创建2个MyDbinfo的实例
myDbinfo1 = MyDbinfo()
myDbinfo2 = MyDbinfo()
if __name__ == __main__:
print(myDbinfo1)
print(myDbinfo2)
打印结果:
<MyDbinfo.MyDbinfo object at 0x7fcc580f4290>
<MyDbinfo.MyDbinfo object at 0x7fcc48021850>
打印结果,是2个不同的物理地址,则说明我们创建的实例,是2个不同的实例。会消耗内存资源。
二、实现单例模式的几种方式
1.使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:
MyDbinfo.py
import yaml
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, r, encoding=utf-8)
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x[DB][host]
self.username=x[DB][username]
self.password=x[DB][password]
self.database=x[DB][database]
self.port=x[DB][port]
self.note = x[DB][note]
myDbinfo = MyDbinfo()
将上面的代码保存在文件 MyDbinfo.py中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
创建2个实例
from MyDbinfo import myDbinfo
myDbinfo1 = myDbinfo
myDbinfo2 = myDbinfo
if __name__ == __main__:
print(myDbinfo1)
print(myDbinfo2)
打印结果:
<MyDbinfo.MyDbinfo object at 0x7fc078111b90>
<MyDbinfo.MyDbinfo object at 0x7fc078111b90>
是单例的
2.使用装饰器
singleton.py
def singleton(cls,*args,**kw):
instances =
def _singleton():
if cls not in instances:
instances[cls] = cls(*args,**kw)
return instances[cls]
return _singleton
MyDbinfo类,加单例模式的注释
MyDbinfo.py
import yaml
from singleton import singleton
@singleton
class MyDbinfo(object):
def __init__(self):
_filePath = "/Users/zhaohui/PycharmProjects/TestSingleton/configuration.yml"
f = open(_filePath, r, encoding=utf-8)
cont = f.read()
x = yaml.load(cont, Loader=yaml.FullLoader)
self.host =x[DB][host]
self.username=x[DB][username]
self.password=x[DB][password]
self.database=x[DB][database]
self.port=x[DB][port]
self.note = x[DB][note]
开始测试
from MyDbinfo import MyDbinfo
myDbinfo1 = MyDbinfo()
myDbinfo2 = MyDbinfo()
if __name__ == __main__:
print(myDbinfo1)
print(myDbinfo2)
打印结果:
<MyDbinfo.MyDbinfo object at 0x7fcd50154290>
<MyDbinfo.MyDbinfo object at 0x7fcd50154290>
为单例。
具体如何实现的:
1、需要先了解装饰器:
python 闭包与装饰器_傲娇的喵酱的博客-CSDN博客
2、我们只需要关注这一块逻辑
3、里面的cls就是我们被装饰的函数
4、_instance 是一个空字典
5、如果被装饰的函数,不在字典里,那我们就cls() 创建一个实例。放到字典里,然后return出去。
如果被装饰的函数,在字典里,直接return出去这个实例。
6、闭包的作用就是让一个变量能够常驻内存。
则_instance = 是常驻内存的(这是重点)
②让函数内部的局部变量始终保持在内存中:
怎么来理解这句话呢?一般来说,函数内部的局部变量在这个函数运行完以后,就会被Python的垃圾回收机制从内存中清除掉。如果我们希望这个局部变量能够长久的保存在内存中,那么就可以用闭包来实现这个功能。
这里借用
@千山飞雪
的例子(来自于:千山飞雪:深入浅出python闭包)。请看下面的代码。
以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。 这里需要说明的是,每次运动的起点都是上次运动结束的终点。
def create(pos=[0,0]):
def go(direction, step):
new_x = pos[0]+direction[0]*step
new_y = pos[1]+direction[1]*step
pos[0] = new_x
pos[1] = new_y
return pos
return go
player = create()
print(player([1,0],10))
print(player([0,1],20))
print(player([-1,0],10))
在这段代码中,player实际上就是闭包go函数的一个实例对象。
它一共运行了三次,第一次是沿X轴前进了10来到[10,0],第二次是沿Y轴前进了20来到 [10, 20],,第三次是反方向沿X轴退了10来到[0, 20]。
这证明了,函数create中的局部变量pos一直保存在内存中,并没有在create调用后被自动清除。
为什么会这样呢?原因就在于create是go的父函数,而go被赋给了一个全局变量,这导致go始终在内存中,而go的存在依赖于create,因此create也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这个时候,闭包使得函数的实例对象的内部变量,变得很像一个类的实例对象的属性,可以一直保存在内存中,并不断的对其进行运算。
以上是关于python 单例模式的主要内容,如果未能解决你的问题,请参考以下文章