Python中实现单例模式的n种方式和原理
作者:mrr 发布时间:2021-01-07 20:45:58
在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种方式和原理网站的支持!
猜你喜欢
- 现在不写asp了这次我将我以前沉淀下的一些函数库共享给大家,希望能给初学者启示,给老手也有所帮助吧.先谢谢大家支持! <%@
- python 打印对象的所有属性值:def prn_obj(obj): print '\n'.join([
- 简介显示大量已经按类别(不是很多)排序的数据但没有类别分界线,用户很难找到所需要的类别。例如,数据库中只有9个类别(8个不同的类别和1个nu
- 人生苦短,菜鸟学Python!今天,我们会一次性分享6个堪称神仙的内置函数。在很多计算机书籍中,它们也通常作为高阶函数来介绍。而我自己在日常
- 在 Python 2 中 xrange() 创建迭代对象的用法是非常流行的。比如: for 循环或者是列表/集合/字典推导式。这个表现十分像
- ORA-01578:Oracle data block corrupted(file # num,block # num)产生原
- 字符串Go语言中的字符串以原生数据类型出现。 Go 语言里的字符串的内部实现使用UTF-8编码。 字符串的值为双引号(")中的内容
- 在ASP输出页面只是调出数据库里内容展现给用户看没有使用SESSION等动态属性,是可以CACHE的,以加快访问速度具体操作方法如下:在as
- 在python3中使用dict.keys()返回的不在是list类型了,也不支持索引,我们可以看一下下面这张图片那么我们应该怎么办呢,其实解
- 正好最近的域名备案通过了,兴起就突然想做一个网页,虽然之前去备案域名也是有这个目的。问过几个人,说用linux上用PHP搭建网站很简单,就试
- 本文实例讲述了php实现比较全的数据库操作类。分享给大家供大家参考。具体如下:<?php class database {  
- 1.API接口:hello world 案例from flask import Flaskfrom flask_restful import
- 该平台会集成UI自动化及api自动化,里面也会涉及到一些简单的HTML等前端,当然都是很基础的东西。在以后的博客里,我会一点点的尽量写详细,
- 由于并不清楚服务器具体地址,只有jupyter 连接的情况下,上传文件。方法一:用Linux命令直接用linux命令,在jupyter中只需
- Python实现模拟时钟代码推荐# coding=utf8import sys, pygame, math, randomfrom pyga
- phpMyAdmin 是一套可以通过WEB来管理 MySQL-server 以及单一数据库的 PHP
- 超如果你急需一个简单的Web Server,但你又不想去下载并安装那些复杂的HTTP服务程序,比如:Apache,ISS等。那么, Pyth
- 使用cookie来判断来访者身份,是否是首次登陆, asp代码实例如下:< %@ LANGUAGE=&q
- 我自己的一个项目,需要同时对65536个文件进行多次写操作。如果先全部打开所有的文件,然后重复写,最后关闭所有的文件。那么第一次写操作全部完
- 一个功能的实现,可以用多种语句来实现,比如说:while语句、for语句、生成器、列表推导、内置函数等实现,然而他们的效率并不一样。写了一个