单例模式

这是一种设计模式

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

单例设计模式

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

单例设计模式的应用场景

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

为什么要单例模式?

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

__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. Samba 远程命令执行漏洞(CVE-2017-7494)

    该漏洞影响Samba 3.5.0之后的所有版本,在4.6.4/4.5.10/4.4.14修复了这个漏洞 use exploit/linux/samba/is_known_pipename set rh ...

  2. Java进阶练习题整理(1)

    1. 检查时异常.非检查时异常.运行时异常. Throwable是一切异常.错误类的超类,Throwable有两个子类,Error(错误)和Exception(异常).Exception异常又分为Ru ...

  3. OI卷题记录

    2021.8.2 LG3386 匈牙利算法 二分图 LG1377 笛卡尔树 题解 2021.8.3 LG2962 \(\text{Meet in middle}\) LG3389 高斯消元 高斯-约旦 ...

  4. vue日记②之兼容各种情况的可跳转链接

    兼容各种情况的可跳转链接 需求 因为聊天气泡颜色原因,发送出去的链接通常模糊不清,而且不能直接跳转,所以我打算已a链接的显示直接抓取所有的网页链接,同时还要兼容富文本框的直接输入图片 这是运行效果 实 ...

  5. 如何看待Android开发的“前景和内卷”

    我们首先来意淫一波 5G时代Android即将崛起,Android将与物联网强强联合,配合上5G信息高速传递的模式,再搭配物联网号召的"万物互通"的旗号,同时各位Android开发 ...

  6. 4.10 Python3 进阶 - 迭代器 & 生成器

    >>返回主目录 源码 from typing import Iterable, Iterator # 可迭代对象:字符串.列表.元组.字典.集合.range().enumerate()等 ...

  7. ElasticSearch版本控制--java实现

    一.前言 最近工作中有这样一个ElasticSearch(以下简称ES)写入的场景,Flink处理完数据实时写入ES.现在需要将一批历史数据通过Flink加载到到ES,有两个点需要保证: 对于历史数据 ...

  8. Java-Mybatis动态SQL整理

    XML映射器 SQL映射文件的几个顶级元素: cache - 该命名空间的缓存配置 cache-ref - 引用其他命名空间的缓存配置 resultMap - 描述如何从数据库结果集中加载对象 sql ...

  9. NOIP 模拟 $12\; \text{简单的区间}$

    题解 签到题 求区间和为 \(k\) 的倍数的区间,我们可以转化为求左右两个端点,其前缀和相等 对于区间最大值,我们可以把其转化为一个值,它能向左,向右扩展的最远边界,一个单调栈即可 我们设一个值 \ ...

  10. 公司新来了一个质量工程师,说团队要保证 0 error,0 warning

    摘要:静态代码检查又称为静态程序分析,是指在不运行计算机程序的条件下,进行程序分析的方法. 本文分享自华为云社区<公司新来了一个质量工程师,说团队要保证 0 error,0 warning> ...