单例模式

这是一种设计模式

  • 设计模式是前任工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案
  • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

单例设计模式

  • 目的:让某一个类创建的实例对象,在整个应用程序中只有唯一的一个实例对象而且该对象易于外界访问,从而方便对实例个数的控制并节约系统资源
  • 每一次执行 类名() 返回的对象,内存地址是相同的

单例设计模式的应用场景

  • 音乐播放器对象
  • 回收站对象
  • 打印机对象
  • .....

为什么要单例模式?

  • 提问:如何保证一个类只有一个实例并且这个实例易于被访问呢?
  • 不使用单例模式:定义一个全局变量可以确保对象随时都可以被访问,但不能防止实例化多个对象
  • 单例模式的出现:类自己负责只能创建一个实例对象,可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法

__new__ 方法

使用 类名() 创建对象时,Python 的解释器首先会调用 __new__ 方法为对象分配内存空间

class PoloBlog:
def __new__(cls, *args, **kwargs):
print("分配内存地址啦") def __init__(self):
print("初始化对象...") blog = PoloBlog()
print(blog) # 输出结果
分配内存地址啦
None

哎,为什么打印对象是 None,而且没有调用到 __init__ 方法呢??下面讲解!

内置的静态方法

__new__ 是一个由 object 基类提供的内置的静态方法

__new__ 主要作用

  • 在内存中为实例对象分配空间
  • 返回对象的引用给 Python 解释器

Python 的解释器获得对象的引用后,将对象的引用作为第一个参数,传递给 __init__ 方法

重写 __new__ 方法

  • 重写的代码是固定的
  • 重写 __new__ 方法一定要在最后 return super().__new__(cls)
  • 如果不 return(像上面代码栗子一样),Python 的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法(__init__)
  • 重点:__new__ 是一个静态方法,在调用时需要主动传递 cls 参数
class PoloBlog:
def __new__(cls, *args, **kwargs):
# 1、自动调用 __new__
print("分配内存地址啦")
# 2、为对象分配空间得到的引用赋值给 instance
instance = super().__new__(cls)
print(id(instance))
# 3、返回对象引用给 Python 解释器
return instance def __init__(self):
print("初始化对象...")
print(id(self)) blog = PoloBlog() # 输出结果
分配内存地址啦
4363809888
初始化对象...
4363809888

可以看到打印的两个内存地址是同一个哦:证明 __new__ 分配的对象引用的确传给了 __init__ 方法的 self 参数

__new__ 实现单例模式

class PoloBlog:
def __new__(cls, *args, **kwargs):
print("分配内存地址啦")
instance = super().__new__(cls)
return instance def __init__(self):
print("初始化对象...") blog = PoloBlog()
blog1 = PoloBlog() print(id(blog))
print(id(blog1)) # 输出结果
4449363040
4449361984

很明显,两个对象各有自己的内存地址;单纯的重写 __new__ 方法并不能实现单例模式

__new__ 实现单例模式的逻辑

单例:在整个应用程序中只有唯一的一个实例对象

  1. 定义一个类属性,来保存单例对象的引用
  2. 重写 __new__ 方法
  3. 如果类属性 is None,则调用父类方法分配内存空间,并赋值给类属性
  4. 如果类属性已有对象引用,则直接返回

单例模式的代码实现

# 单例模式
class PoloBlog:
instance = None def __new__(cls, *args, **kwargs):
# 1、判断类属性是否为 None
if cls.instance is None:
# 2、为空,调用父类方法,给对象分配内存空间,并赋值给类属性
cls.instance = super().__new__(cls) # 3、如果不为空,则直接返回类属性保存的对象引用
return cls.instance def __init__(self):
pass blog = PoloBlog()
blog1 = PoloBlog()
blog2 = PoloBlog()
print(id(blog), id(blog1), id(blog2)) # 输出结果
4336982096 4336982096 4336982096

可以看到创建的三个实例对象其实都是同一个,这就是单例模式!

初始化工作仅执行一次

在每次使用类名()创建对象时,Python 的解释器都会自动调用两个方法

  • __new__ 分配空间
  • __init__ 对象初始化

上面所说的单例模式,是针对 __new__ 方法进行重写的,创建多个实例对象都会得到同一个实例对象

但是:初始化方法还是会被多次调用

class PoloBlog:
instance = None def __new__(cls, *args, **kwargs):
if cls.instance is None: cls.instance = super().__new__(cls) return cls.instance def __init__(self):
print("yep") blog = PoloBlog()
blog1 = PoloBlog()
blog2 = PoloBlog() # 输出结果
yep
yep
yep

假设想让初始化动作只执行一次呢?

其也很简单,和单例模式的解决思路差不多

  1. 定义一个类属性标记是否执行过初始化动作,初始值为 False
  2. 在 __init__ 方法中,判断类属性,如果 False,则执行初始化动作,然后设置为 True
  3. 如果 True 则直接跳过不执行
# 单例模式
class PoloBlog:
instance = None
init_flag = None def __new__(cls, *args, **kwargs):
if cls.instance is None: cls.instance = super().__new__(cls) return cls.instance def __init__(self):
# 1、判断是否为 True,因为是实例方法,所以调用类属性要通过类对象
if PoloBlog.init_flag:
# 2、如果 True,直接跳过不执行后续初始化动作
return
# 3、如果 False,则执行
print("初始化动作")
# 4、修改 init_flag
PoloBlog.init_flag = True blog = PoloBlog()
blog1 = PoloBlog()
blog2 = PoloBlog() # 输出结果
初始化动作

Python - 面向对象编程 - __new()__ 和单例模式 的更多相关文章

  1. python 面向对象编程学习

    1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...

  2. python 面向对象编程(一)

    一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...

  3. Python 面向对象编程——访问限制

    <无访问限制的对象> 在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑.但是,从前面Student类的定义来看(见:Py ...

  4. Python 面向对象编程基础

    Python 面向对象编程基础 虽然Pthon是解释性语言,但是Pthon可以进行面向对象开发,小到 脚本程序,大到3D游戏,Python都可以做到. 一类: 语法: class 类名: 类属性,方法 ...

  5. python面向对象编程学习

    python面向对象编程 基本概念理解 面向对象编程--Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作 ...

  6. Python面向对象编程指南

    Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...

  7. python面向对象编程进阶

    python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 1 ...

  8. Python面向对象编程(下)

    本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...

  9. Python 面向对象编程 继承 和多态

    Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...

随机推荐

  1. Couchdb 任意命令执行漏洞(CVE-2017-12636)

    影响版本:小于 1.7.0 以及 小于 2.1.1 该漏洞是需要登录用户方可触发,如果不知道目标管理员密码,可以利用CVE-2017-12635先增加一个管理员用户 依次执行如下请求即可触发任意命令执 ...

  2. 大数据学习(06)——Ozone介绍

    前面几篇文章把Hadoop常用的模块都学习了,剩下一个新模块Ozone,截止到今天最新版本是0.5.0Beta,还没出正式版.好在官方网站有文档,还是中文版的,但是中文版资料没有翻译完整,我试着把它都 ...

  3. oracle(enquences & latches )lock (oracle 锁大全)

    资料来自官方文档: https://docs.oracle.com/database/121/CNCPT/consist.htm#CNCPT1333 https://docs.oracle.com/d ...

  4. centos ansible常用命令

    ansible在日常运维中经常使用,特别是批量执行多台服务器的时候,有效减小重复的操作成本,以下从安装到使用仅讲解工作中常用的几种方式,模块很多功能很强大,但不做全面讨论. ansible安装 在ce ...

  5. Hybrid接口

    目录 一.Hybrid接口 1.1 VLan的基本概念 1.2 Hybrid接口特点 1.3 Hybrid接口工作原理 1.4 Hybrid配置 一.Hybrid接口 1.1 VLan的基本概念 特点 ...

  6. AuthorizationFailed""The client '***' with object id '***' does not have authorization to perform action 'or the scope is invalid. If access was recently granted, please refresh your credentials

    Warning  SyncLoadBalancerFailed  4m9s (x11 over 29m)   service-controller  Error syncing load balanc ...

  7. 2020年度钻石C++C学习笔记(2)--《博学谷》

    2020年度钻石C++C--<博学谷> 1.以下标示符中命名合法的是A A.__A__ B.ab.c C.@rp D.2Y_ 2.设 a 和 b 均为 double 型变量,且a=5.5. ...

  8. jdk、jre环境变量配置

    1 jdk和jre的区别: (jdk:Java 开发工具包) (jre:Java 的运行环境) 只需这么记就可以了,想深入了解得自行查询相关资料 2 jdk是包含jre的,所以只需下载jdk. 官方网 ...

  9. 物理机安装ESXi并优化部署虚拟机

    物理机配置 CPU,BIOS中启用虚拟化(VT-X) 内存和硬盘,内存尽量大.硬盘最好SSD,内存的大小和硬盘的速度直接决定了虚拟机运行的快慢 网络,至少一块千兆网卡(vSphere7.0版本以后支持 ...

  10. 【odoo】【知识点】生成pdf文件时缺少样式的问题

    欢迎转载,但需标注出处,谢谢! 背景 近期在客户的项目中发现在自定义报表样式的时候,存在渲染为html正常,但是在生成pdf的时候,缺少样式的情况. 分析 涉及到的odoo源码中的ir_actions ...