单例模式是一个软件的设计模式,为了保证一个类,无论调用多少次产生的实例对象,都是指向同一个内存地址,仅仅只有一个实例(只有一个对象)。

实现单例模式的手段有很多种,但总的原则是保证一个类只要实例化一个对象,下一次再实例的时候就直接返回这个对象,不再做实例化的操作。所以这里面的关键一点就是,如何判断这个类是否实例化过一个对象

这里介绍两类方式:

  • 一类是通过模块导入的方式;
  • 一类是通过魔法方法判断的方式;
# 基本原理:
- 第一类通过模块导入的方式,借用了模块导入时的底层原理实现。
- 当一个模块(py文件)被导入时,首先会执行这个模块的代码,然后将这个模块的名称空间加载到内存。
- 当这个模块第二次再被导入时,不会再执行该文件,而是直接在内存中找。
- 于是,如果第一次导入模块,执行文件源代码时实例化了一个类,那再次导入的时候,就不会再实例化。 - 第二类主要是基于类和元类实现,在'对象'的魔法方法中判断是否已经实例化过一个对象
- 这类方式,根据实现的手法不同,又分为不同的方法,如:
- 通过类的绑定方法;通过元类;通过类下的__new__;通过装饰器(函数装饰器,类装饰器)实现等。

下面分别介绍这几种不同的实现方式,仅供参考实现思路,不做具体需求。

通过模块导入

# cls_singleton.py
class Foo(object):
pass instance = Foo() # test.py
import cls_singleton obj1 = cls_singleton.instance
obj2 = cls_singleton.instance
print(obj1 is obj2) # 原理:模块第二次导入从内存找的机制

通过类的绑定方法

class Student:
_instance = None # 记录实例化对象 def __init__(self, name, age):
self.name = name
self.age = age @classmethod
def get_singleton(cls, *args, **kwargs):
if not cls._instance:
cls._instance = cls(*args, **kwargs)
return cls._instance stu1 = Student.get_singleton('jack', 18)
stu2 = Student.get_singleton('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__) # 原理:类的绑定方法是第二种实例化对象的方式,
# 第一次实例化的对象保存成类的数据属性 _instance,
# 第二次再实例化时,在get_singleton中判断已经有了实例对象,直接返回类的数据属性 _instance

通过魔法方法__new__

class Student:

    _instance = None

    def __init__(self, name, age):
self.name = name
self.age = age def __new__(cls, *args, **kwargs):
# if cls._instance:
# return cls._instance # 有实例则直接返回
# else:
# cls._instance = super().__new__(cls) # 没有实例则new一个并保存
# return cls._instance # 这个返回是给是给init,再实例化一次,也没有关系 if not cls._instance: # 这是简化的写法,上面注释的写法更容易提现判断思路
cls._instance = super().__new__(cls)
return cls._instance stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__) # 原理:和方法2类似,将判断的实现方式,从类的绑定方法中转移到类的__new__中
# 归根结底都是 判断类有没有实例,有则直接返回,无则实例化并保存到_instance中。

通过元类

class Mymeta(type):

    def __init__(cls, name, bases, dic):
super().__init__(name, bases, dic)
cls._instance = None # 将记录类的实例对象的数据属性放在元类中自动定义了 def __call__(cls, *args, **kwargs): # 此call会在类被调用(即实例化时触发)
if cls._instance: # 判断类有没有实例化对象
return cls._instance
else: # 没有实例化对象时,控制类造空对象并初始化
obj = cls.__new__(cls, *args, **kwargs)
obj.__init__(*args, **kwargs)
cls._instance = obj # 保存对象,下一次再实例化可以直接返回而不用再造对象
return obj class Student(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__) # 原理:类定义时会调用元类下的__init__,类调用(实例化对象)时会触发元类下的__call__方法
# 类定义时,给类新增一个空的数据属性,
# 第一次实例化时,实例化之后就将这个对象赋值给类的数据属性;第二次再实例化时,直接返回类的这个数据属性
# 和方式3的不同之处1:类的这个数据属性是放在元类中自动定义的,而不是在类中显示的定义的。
# 和方式3的不同之处2:类调用时触发元类__call__方法判断是否有实例化对象,而不是在类的绑定方法中判断

函数装饰器

def singleton(cls):
_instance_dict = {} # 采用字典,可以装饰多个类,控制多个类实现单例模式 def inner(*args, **kwargs):
if cls not in _instance_dict:
_instance_dict[cls] = cls(*args, **kwargs)
return _instance_dict.get(cls)
return inner @singleton
class Student:
def __init__(self, name, age):
self.name = name
self.age = age # def __new__(cls, *args, **kwargs): # 将方法3的这部分代码搬到了函数装饰器中
# if not cls._instance:
# cls._instance = super().__new__(cls)
# return cls._instan stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__)

类装饰器

class SingleTon:
_instance_dict = {} def __init__(self, cls_name):
self.cls_name = cls_name def __call__(self, *args, **kwargs):
if self.cls_name not in SingleTon._instance_dict:
SingleTon._instance_dict[self.cls_name] = self.cls_name(*args, **kwargs)
return SingleTon._instance_dict.get(self.cls_name) @SingleTon # 这个语法糖相当于Student = SingleTon(Student),即Student是SingleTon的实例对象
class Student:
def __init__(self, name, age):
self.name = name
self.age = age stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__) # 原理:在函数装饰器的思路上,将装饰器封装成类。
# 程序执行到与语法糖时,会实例化一个Student对象,这个对象是SingleTon的对象。
# 后面使用的Student本质上使用的是SingleTon的对象。
# 所以使用Student('jack', 18)来实例化对象,其实是在调用SingleTon的对象,会触发其__call__的执行
# 所以就在__call__中,判断Student类有没有实例对象了。

Python的6种方式实现单例模式的更多相关文章

  1. PHP类名获取的几种方式及单例模式实现

    参考:https://www.cnblogs.com/water0729/p/5803217.html <?php class foo { static public function test ...

  2. python学习-- 两种方式查看自己的Django版本

    [第一种方式] Windows系统下 按住Windows按键 + R 进入搜索:搜索CMD进入控制台:输入Python进入Python解释器 Linux系统下 直接使用终端调用Python解释器 接下 ...

  3. bat批处理执行python 的几种方式

    第一种方式:@echo off C: cd C:\Users\administrator\Desktopstart python apidemo.py exit第二种方式: start cmd /K ...

  4. 四、执行Python的两种方式

    第一种 交互式 ,在cmd中运行 · jupyter对这一种进行了封装 优点: 直接输出结果 缺点: 无法保存 第二种 命令式,通过cmd中输入python3文本 txt文件可以,py文件也可以,命令 ...

  5. Beanutils工具类,封装数据的三种方式,单例模式

    org.apache.commons.beanutils.Beanutils; Beanutils setProperty(Object obj,String name,Object value) O ...

  6. PyCharm 中文教程 01:运行 Python 的四种方式

    <PyCharm 中文指南>在线阅读: http://pycharm.iswbm.com/ Github 项目主页: https://github.com/iswbm/pych... 1. ...

  7. Python之面向对象之单例模式的四种方式

    一.内容 保证一个类只有一个实例,并提供一个访问它的全局访问点 二.角色 单利 三.使用场景 当类只有一个实例而且客户可以从一个众所周知的访问点访问它时 比如:数据库链接.Socket创建链接 四.优 ...

  8. 执行python解释器的两种方式

    执行python解释器的两种方式 1.交互式 python是高级语言,是解释型语言,逐行翻译,写一句翻译一句 print ('hello world') 2.命令行式 python和python解释器 ...

  9. [Python]xlrd 读取excel 日期类型2种方式

    有个excle表格须要做一些过滤然后写入数据库中,可是日期类型的cell取出来是个数字,于是查询了下解决的办法. 主要的代码结构 data = xlrd.open_workbook(EXCEL_PAT ...

随机推荐

  1. 【干货】Keras学习资源汇总

    目录: Keras简介 Keras学习手册 Keras学习视频 Keras代码案例 Keras&NLP Keras&CV Keras项目 一.Keras简介 Keras是Python中 ...

  2. nim博弈 LightOJ - 1253

    主要是写一下nim博弈的理解,这个题有点奇怪,不知道为什么判断奇偶性,如果有大佬知道还请讲解一下. //nim博弈 //a[0]~a[i] 异或结果为k 若k=0 则为平衡态 否则为非平衡态 //平衡 ...

  3. Ubuntu复制粘贴快捷键

    复制:[Ctrl+Insert] 粘贴:[Shitf+Insert]

  4. Downgrade ASM DATABASE_COMPATIBILITY (from 11.2.0.4.0 to 11.2.0.0.0) on 12C CRS stack.

    使用Onecommand完成快速Oracle 12c RAC部署后 发现ASM database compatibilty无法设置,默认为11.2.0.4.0. 由于我们还有些数据库低于这个版本,所以 ...

  5. spring-cloud-gateway降级

    前言 本文主要研究一下 spring cloud gateway 如何集成 hystrix. 当下游接口负载很大,或者接口不通等其他原因导致超时,如果接口不熔断的话将会影响到下游接口得不到喘息,网关也 ...

  6. D - 淡黄的长裙 HDU - 4221(贪心)

    D - 淡黄的长裙 HDU - 4221(贪心) James is almost mad! Currently, he was assigned a lot of works to do, so ma ...

  7. 最大子矩阵hdu1559(二维前缀和)

    最大子矩阵hdu1559 Problem Description 给你一个m×n的整数矩阵,在上面找一个x×y的子矩阵,使子矩阵中所有元素的和最大. Input 输入数据的第一行为一个正整数T,表示有 ...

  8. 聊起 BigTable,让你不再胆怯

    谷歌“三驾马车”的出现,才真正把我们带入了大数据时代,并指明了大数据的发展方向. GFS 作为其中一驾宝车,解决了大数据存储的难题.它能够把大量廉价的普通机器,聚在一起,充分让每台廉价的机器发挥光和热 ...

  9. es6声明一个类

    js语言的传统方式是通过定义构造函数,生成心得对象.是一种基于原型的面向对象系统.在es6中增加了class类的概念,可以使用class关键字来声明一个类.之后用这个类来实例化对象. 构造函数示例 c ...

  10. 构建Mysql服务器

    <综合>构建Mysql服务器 构建MySQL服务器 数据库基本管理 MySQL 数据类型 表结构的调整 1 构建MySQL服务器 1.1 问题 本案例要求熟悉MySQL官方安装包的使用,快 ...