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学习之编写查询ip程序
Feb 27 Python
Python生成随机密码的方法
Jun 16 Python
python修改list中所有元素类型的三种方法
Apr 09 Python
python 除法保留两位小数点的方法
Jul 16 Python
Python二进制串转换为通用字符串的方法
Jul 23 Python
Python装饰器模式定义与用法分析
Aug 06 Python
python读取目录下最新的文件夹方法
Dec 24 Python
决策树剪枝算法的python实现方法详解
Sep 18 Python
详解Python在使用JSON时需要注意的编码问题
Dec 06 Python
用Python爬取英雄联盟的皮肤详细示例
Dec 06 Python
Python实现学生管理系统并生成exe可执行文件详解流程
Jan 22 Python
关于的python五子棋的算法
May 02 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数据的三种方法
2006/10/09 PHP
站长助手-网站web在线管理程序 v1.0 下载
2007/05/12 PHP
PHP中防止SQL注入实现代码
2011/02/19 PHP
PHP编程中尝试程序并发的几种方式总结
2016/03/21 PHP
深入理解JavaScript系列(10) JavaScript核心(晋级高手必读篇)
2012/01/15 Javascript
js iframe跨域访问(同主域/非同主域)分别深入介绍
2013/01/24 Javascript
JQuery入门——用bind方法绑定事件处理函数应用介绍
2013/02/05 Javascript
完美解决IE低版本不支持call与apply的问题
2013/12/05 Javascript
js清除input中type等于file的值域(示例代码)
2013/12/24 Javascript
PHP实现的各种中文编码转换类分享
2015/01/23 Javascript
dul无法加载bootstrap实现unload table/user恢复
2016/09/29 Javascript
javascript之with的使用(阿里云、淘宝使用代码分析)
2016/10/11 Javascript
nodejs利用http模块实现银行卡所属银行查询和骚扰电话验证示例
2016/12/30 NodeJs
JS小数转换为整数的方法分析
2017/01/07 Javascript
JS查找英文文章中出现频率最高的单词
2017/03/20 Javascript
Vue.Draggable实现拖拽效果
2020/07/29 Javascript
Node.js  事件循环详解及实例
2017/08/06 Javascript
详解jQuery同步Ajax带来的UI线程阻塞问题及解决办法
2017/08/09 jQuery
基于模板引擎Jade的应用(详解)
2017/12/12 Javascript
vue 根据选择条件显示指定参数的例子
2019/11/09 Javascript
vuejs实现下拉框菜单选择
2020/10/23 Javascript
Vue实现图书管理小案例
2020/12/03 Vue.js
python实现模拟按键,自动翻页看u17漫画
2015/03/17 Python
python 产生token及token验证的方法
2018/12/26 Python
对python 多线程中的守护线程与join的用法详解
2019/02/18 Python
基于django传递数据到后端的例子
2019/08/16 Python
Python内置方法和属性应用:反射和单例(推荐)
2020/06/19 Python
Python与C/C++的相互调用案例
2021/03/04 Python
应届生船舶驾驶求职信
2013/10/19 职场文书
四议两公开实施方案
2014/03/28 职场文书
《她是我的朋友》教学反思
2014/04/26 职场文书
2014年建筑工作总结
2014/11/26 职场文书
2015年端午节国旗下演讲稿
2015/03/19 职场文书
2015年十月一日放假通知
2015/08/18 职场文书
人为什么会“幸灾乐祸”?
2019/08/06 职场文书
SQLyog的下载、安装、破解、配置教程(MySQL可视化工具安装)
2022/09/23 MySQL