单例模式

这是一种设计模式

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

单例设计模式

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

单例设计模式的应用场景

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

为什么要单例模式?

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

__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. Mybatis学习笔记-分页

    为何要分页 减少数据处理量 便于前端展示数据 使用Limit分页 语法结构 SELECT * FROM user LIMIT startIndex,pageSize; SELECT * FROM us ...

  2. Linux上搭建zookeeper服务注册中心

    .personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...

  3. Mybatis源码解析2—— 实例搭建

    大家好,我是可乐. 上篇文章给大家撸了一遍用 JDBC 直接操作数据库的实例,还只是简单写了一个查询的接口,其代码量就已经很大了,并且可乐还给大家分析了直接使用 JDBC 带来的一些问题,总之是一种反 ...

  4. awk-06-常用内置函数

    常用内置函数 示例 1.int 2.sqrt 3.rand rand()并不是每次运行都会产生一个随机数,会一直保持不变 所以需要srand() 函数一起 但是还是有很大的几率会生成一样 4.asor ...

  5. MongoDB-01-基础

    mongodb逻辑结构 Mongodb 逻辑结构 MySQL逻辑结构 库 (database) 库 集合(collection) 表 文档(document) 数据行 安装部署 1 系统准备 1 这里 ...

  6. 一篇文章搞懂密码学基础及SSL/TLS协议

    SSL协议是现代网络通信中重要的一环,它提供了传输层上的数据安全.为了方便大家的理解,本文将先从加密学的基础知识入手,然后展开对SSL协议原理.流程以及一些重要的特性的详解,最后会扩展介绍一下国密SS ...

  7. jumpserver堡垒机(2.4)部署

    jumpserver 2.4.0 部署 jumpserver 官网: https://www.jumpserver.org/ Jumpserver介绍 JumpServer 是全球首款完全开源的堡垒机 ...

  8. vue项目中踩过的element的坑

    前言:在现在这种大的社会背景下,人们的需求更加的个性化了,而之前为了解放开发复杂的原生开发状态,现有的组件库已经远远不能满足人们高质量的需求了,这两天开发发现了一些element UI交互上的缺陷,当 ...

  9. vs code 调试angular2

    调试步骤: 1.安装nodejs 2.安装vscode 3.vscode安装debugger for chrome插件 4.选择调试->打开调试配置,选择chrome配置,打开lauch.jso ...

  10. 《深入理解Spring Cloud与微服务构建》学习笔记(二十)~配置中心Spring Cloud Config

    本例重新创建项目,构建一个空的mavan工程. 一.Config Server 从本地读取配置文件 新建一个moudle config_server ,pom添加依赖   <dependency ...