基于Python中单例模式的几种实现方式及优化详解


Posted in Python onJanuary 09, 2018

单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

在 Python 中,我们可以用多种方法来实现单例模式

实现单例模式的几种方式

1.使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

mysingleton.py
class Singleton(object):
 def foo(self):
  pass
singleton = Singleton()

将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象

from a import singleton

2.使用类

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

一般情况,大家以为这样就完成了单例模式,但是这样当使用多线程时会存在问题

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()

程序执行后,打印结果如下:

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

看起来也没有问题,那是因为执行速度过快,如果在init方法中有一些IO操作,就会发现问题了,下面我们通过time.sleep模拟

我们在上面__init__方法中加入以下代码:

def __init__(self): import time time.sleep(1)

重新执行程序后,结果如下

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

问题出现了!按照以上方式创建的单例,无法支持多线程

解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全

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)

打印结果如下:

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了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

这样,一个可以支持多线程的单例模式就完成了

import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
  time.sleep(1)
 @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

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)

这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()

如果用 obj=Singleton() ,这种方式得到的不是单例

3.基于__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, *args, **kwargs)
  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()

打印结果如下:

<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>

采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()

4.基于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):
  pass
 def __new__(cls, *args, **kwargs):
  return object.__new__(cls, *args, **kwargs)
obj = Foo()

实现单例模式

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)

以上这篇基于Python中单例模式的几种实现方式及优化详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
用python实现的可以拷贝或剪切一个文件列表中的所有文件
Apr 30 Python
使用grappelli为django admin后台添加模板
Nov 18 Python
详解在Python的Django框架中创建模板库的方法
Jul 20 Python
利用python获取当前日期前后N天或N月日期的方法示例
Jul 30 Python
Python根据指定日期计算后n天,前n天是哪一天的方法
May 29 Python
关于python写入文件自动换行的问题
Jun 23 Python
python训练数据时打乱训练数据与标签的两种方法小结
Nov 08 Python
python抓取网页内容并进行语音播报的方法
Dec 24 Python
关于python tushare Tkinter构建的简单股票可视化查询系统(Beta v0.13)
Oct 19 Python
python 删除系统中的文件(按时间,大小,扩展名)
Nov 19 Python
python 装饰器重要在哪
Feb 14 Python
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
May 28 Python
浅谈Python2获取中文文件名的编码问题
Jan 09 #Python
在CentOS6上安装Python2.7的解决方法
Jan 09 #Python
浅谈Matplotlib简介和pyplot的简单使用——文本标注和箭头
Jan 09 #Python
用tensorflow实现弹性网络回归算法
Jan 09 #Python
Python+matplotlib实现计算两个信号的交叉谱密度实例
Jan 08 #Python
python matplotlib 注释文本箭头简单代码示例
Jan 08 #Python
Python自定义简单图轴简单实例
Jan 08 #Python
You might like
php stream_get_meta_data返回值
2013/09/29 PHP
PHP实现文件下载断点续传详解
2014/10/15 PHP
php中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
PHP文件操作方法汇总
2015/07/01 PHP
php+ajax无刷新上传图片实例代码
2015/11/17 PHP
PHP实现的mysql主从数据库状态检测功能示例
2017/07/20 PHP
IE6-IE9中tbody的innerHTML不能赋值的解决方法
2014/09/26 Javascript
提升PHP安全:8个必须修改的PHP默认配置
2014/11/17 Javascript
node.js中的console.assert方法使用说明
2014/12/10 Javascript
完美实现八种js焦点轮播图(上篇)
2016/07/18 Javascript
jQuery实现带延时功能的水平多级菜单效果【附demo源码下载】
2016/09/21 Javascript
JS仿京东移动端手指拨动切换轮播图效果
2020/04/10 Javascript
jQuery Validate让普通按钮触发表单验证的方法
2016/12/15 Javascript
js css自定义分页效果
2017/02/24 Javascript
深入理解vue Render函数
2017/07/19 Javascript
vue组件生命周期详解
2017/11/07 Javascript
在Vue中使用highCharts绘制3d饼图的方法
2018/02/08 Javascript
react-native 圆弧拖动进度条实现的示例代码
2018/04/12 Javascript
vue+axios+mock.js环境搭建的方法步骤
2018/08/28 Javascript
利用jsonp解决js读取本地json跨域的问题
2018/12/11 Javascript
JQuery样式与属性设置方法分析
2019/12/07 jQuery
react 生命周期实例分析
2020/05/18 Javascript
[01:03:41]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第三场 2月2日
2021/03/11 DOTA
python编写的最短路径算法
2015/03/25 Python
利用Python的Django框架中的ORM建立查询API
2015/04/20 Python
python 实现提取某个索引中某个时间段的数据方法
2019/02/01 Python
python安装gdal的两种方法
2019/10/29 Python
阿姆斯特丹杜莎夫人蜡像馆官方网站:Madame Tussauds Amsterdam
2019/03/12 全球购物
美国宠物护理专家:Revival Animal Health
2020/01/05 全球购物
社区党总支书记先进事迹材料
2014/01/24 职场文书
顶撞老师检讨书
2014/02/07 职场文书
大学新生入学教育方案
2014/05/16 职场文书
村级四风对照检查材料
2014/08/24 职场文书
商务邀请函
2015/01/30 职场文书
党校培训学习心得体会
2016/01/06 职场文书
高考要来啦!用Python爬取历年高考数据并分析
2021/06/03 Python