Python中实现单例模式的n种方式和原理


Posted in Python onNovember 14, 2018

在Python中如何实现单例模式?这可以说是一个经典的Python面试题了。这回我们讲讲实现Python中实现单例模式的n种方式,和它的原理。

什么是单例模式

维基百科 中说:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

在日常编程中,最常用的地方就在于配置类了。举个例子:

from config import config
print(config.SQLALCHEMY_DB_URI)

我们当然是希望 config 在全局中都是唯一的,那么最简单的实现单例的方式就出来了:使用一个全局变量。

实现单例的方式

全局变量

我们在一个模块中实现配置类:

# config.py
class Config:
  def __init__(self, SQLALCHEMY_DB_URI):
    self.SQLALCHEMY_DB_URI = SQLALCHEMY_DB_URI
config = Config("mysql://xxx")

当然这只是一个例子。真正实现的时候我们肯定不会这样做,因为 __init__ 太难写了。也许我们可以考虑 Python 3.7 中引入的 dataclass :

# config.py
from dataclasses import dataclass
@dataclass
class Config:
    SQLALCHEMY_DB_URI = SQLALCHEMY_DB_URI
config = Config(SQLALCHEMY_DB_URI ="mysql://")

通过使用全局变量,我们在所有需要引用配置的地方,都使用 from config import config 来导入,这样就达到了全局唯一的目的。

使用metaclass

class Singleton(type):
  _instances = {}
  def __call__(cls, *args, **kwargs):
    if cls not in cls._instances:
      cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
    return cls._instances[cls]
class Config(metaclass=Singleton):
  def __init__(self, SQLALCHEMY_DB_URI):
    self.SQLALCHEMY_DB_URI = SQLALCHEMY_DB_URI

metaclass 是类的类,在Python中,instance是实例,class是类,metaclass是类的类。instance是class实例化的结果,而class是metaclass实例化的结果。因此, Config 在被实例化的时候,就会调用 Singleton.__call__ , 所以所有 Config() 的地方,最后都会返回同一个对象。

重写 __new__

class Singleton(object):
  _instance = None
  def __new__(class_, *args, **kwargs):
    if not isinstance(class_._instance, class_):
      class_._instance = object.__new__(class_, *args, **kwargs)
    return class_._instance
class Config(Singleton, BaseClass):
  pass

Python中,类实例化的过程是先执行 Config.__new__ 生成实例,然后执行 实例.__init__ 进行初始化的,所以通过重写 __new__ 也可以达到所有调用 Config() 的地方都返回同一个对象。

使用装饰器

def singleton(class_):
  class class_w(class_):
    _instance = None
    def __new__(class_, *args, **kwargs):
      if class_w._instance is None:
        class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs)
        class_w._instance._sealed = False
      return class_w._instance
    def __init__(self, *args, **kwargs):
      if self._sealed:
        return
      super(class_w, self).__init__(*args, **kwargs)
      self._sealed = True
  class_w.__name__ = class_.__name__
  return class_w
@singleton
class Config(BaseClass):
  pass

使用装饰器也能达到这样的目的,即:有闭包存储了实例,在每次调用 Config() 之前,检查该实例,如果已经初始化过,那么就直接返回,否则则调用 Config() 进行初始化,然后存储。

总结

看完了这四种实现单例的方式,不知道你有没有发现他们都有一个共同点,即:在真正调用 Config() 之前进行一些拦截操作,来保证返回的对象都是同一个:

  • 全局变量:不直接调用 Config() ,而使用同一个全局变量
  • 使用metaclass:metaclass重写 __call__ 来保证每次调用 Config() 都会返回同一个对象
  • 重写 __new__ :重写 __new__ 来保证每次调用 Config() 都会返回同一个对象
  • 使用装饰器:使用装饰器来保证每次调用 Config() 都会返回同一个对象

以上所述是小编给大家介绍的Python中实现单例模式的n种方式和原理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
python实现在无须过多援引的情况下创建字典的方法
Sep 25 Python
Python获取单个程序CPU使用情况趋势图
Mar 10 Python
初步剖析C语言编程中的结构体
Jan 16 Python
浅谈Pandas 排序之后索引的问题
Jun 07 Python
python tkinter界面居中显示的方法
Oct 11 Python
python try except 捕获所有异常的实例
Oct 18 Python
浅谈python下含中文字符串正则表达式的编码问题
Dec 07 Python
Python 确定多项式拟合/回归的阶数实例
Dec 29 Python
Python集中化管理平台Ansible介绍与YAML简介
Jun 12 Python
python如何制作英文字典
Jun 25 Python
python3 sleep 延时秒 毫秒实例
May 04 Python
浅析Python 抽象工厂模式的优缺点
Jul 13 Python
解决Python print输出不换行没空格的问题
Nov 14 #Python
python3 实现一行输入,空格隔开的示例
Nov 14 #Python
python抓取京东小米8手机配置信息
Nov 13 #Python
python输入整条数据分割存入数组的方法
Nov 13 #Python
在Python中输入一个以空格为间隔的数组方法
Nov 13 #Python
python 输入一个数n,求n个数求乘或求和的实例
Nov 13 #Python
python判断完全平方数的方法
Nov 13 #Python
You might like
用php和MySql来与ODBC数据连接
2006/10/09 PHP
php运行出现Call to undefined function curl_init()的解决方法
2010/11/02 PHP
简明json介绍
2008/09/28 Javascript
JavaScript 学习笔记(四)
2009/12/31 Javascript
Ext.get() 和 Ext.query()组合使用实现最灵活的取元素方式
2011/09/26 Javascript
一个简单的Ext.XTemplate的实例代码
2012/03/18 Javascript
jquery如何实现锚点链接之间的平滑滚动
2013/12/02 Javascript
js和jquery如何获取图片真实的宽度和高度
2014/09/28 Javascript
JavaScript设计模式之原型模式(Object.create与prototype)介绍
2014/12/28 Javascript
jQuery实现的五子棋游戏实例
2015/06/13 Javascript
JAVA四种基本排序方法实例总结
2015/07/24 Javascript
jQuery mobile在页面加载时添加加载中效果 document.ready 和window.onload执行顺序比较
2016/07/14 Javascript
JavaScript实现二分查找实例代码
2017/02/22 Javascript
详解js静态资源文件请求的处理
2017/08/01 Javascript
详解React路由传参方法汇总记录
2020/11/29 Javascript
JavaScript实现表单验证功能
2020/12/09 Javascript
Python psutil模块简单使用实例
2015/04/28 Python
Python的gevent框架的入门教程
2015/04/29 Python
深入理解 Python 中的多线程 新手必看
2016/11/20 Python
关于Python中浮点数精度处理的技巧总结
2017/08/10 Python
对python中的float除法和整除法的实例详解
2019/07/20 Python
python读取word 中指定位置的表格及表格数据
2019/10/23 Python
Python基于class()实现面向对象原理详解
2020/03/26 Python
Python中pass的作用与使用教程
2020/11/13 Python
详解CSS3选择器的使用方法汇总
2015/11/24 HTML / CSS
CSS3+HTML5+JS 实现一个块的收缩与展开动画效果
2020/11/17 HTML / CSS
Html5无刷新修改browser Url的方法
2014/01/15 HTML / CSS
德国鞋子网上商店:Omoda.de
2017/03/31 全球购物
德国旅行、体验和活动的预订平台:Watado
2019/12/04 全球购物
职工趣味运动会方案
2014/02/10 职场文书
超市活动计划书
2014/04/24 职场文书
会计工作态度自我评价
2015/03/06 职场文书
2015年教师党员承诺书
2015/04/27 职场文书
小学语文的各类谚语(70首)
2019/08/15 职场文书
http通过StreamingHttpResponse完成连续的数据传输长链接方式
2022/02/12 Python
Win11使用CAD卡顿或者致命错误怎么办?Win11无法正常使用CAD的解决方法
2022/07/23 数码科技