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、我们只需要关注这一块逻辑

python

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 单例模式的主要内容,如果未能解决你的问题,请参考以下文章

Python中的单例模式

Python实现单例模式

python-单例模式

Python中的单例模式

Python 单例模式

Python设计模式——单例模式