🗒️Python面向对象编程12—new方法实现单例模式
00 分钟
2024-8-8
2024-8-8
type
status
date
slug
summary
tags
category
icon
password

函数 def new(cls, *args, **kwargs) 解释

__new__ 方法在 Python 中是一个特殊的类方法,用于控制对象的创建过程。它在实例化一个对象时首先被调用,在 __init__ 方法之前。具体来说,__new__ 是负责返回一个类的实例对象的实际方法,而 __init__ 是用来初始化该实例的。

详细解释

1. __new__ 方法的签名

  • cls: 类本身。与实例方法的 self 相对应。
  • args: 位置参数。
  • *kwargs: 关键字参数。
__new__ 方法是一个类方法,因为它的第一个参数是 cls,而不是 self

2. __new__ 方法的作用

__new__ 方法的主要作用是控制对象的创建过程。它通常用于实现单例模式或其他需要控制实例创建的情况。

3. 单例模式中的 __new__

下面是一个完整的示例代码,展示了如何使用 __new__ 方法实现单例模式:

代码解释

  1. 类变量 instance:
      • instance 用于存储类的单个实例。初始值为 None
  1. __new__ 方法:
      • cls.instance 是否为 None 来判断是否已经存在实例。
      • 如果 cls.instanceNone,调用 super().__new__(cls) 创建一个新的实例,并将其赋值给 cls.instance
      • 如果 cls.instance 不为 None,直接返回 cls.instance,即返回已经创建的实例。
  1. __init__ 方法:
      • __init__ 用于初始化实例变量 name。无论 __new__ 返回了新的实例还是已有的实例,__init__ 都会被调用。
  1. add_task 方法:
      • 用于向任务列表 tasks 中添加任务,并输出相关信息。
  1. 实例化和任务添加:
      • p1 = Printer("Word app")
      • p2 = Printer("Excel app")
      • p3 = Printer("PPT app")
      • 由于单例模式的实现,p1p2p3 实际上都是同一个实例。

输出结果

这表明 p1p2p3 都是同一个对象。
 
 
 
 

每次单独创建实例情况(第二个版本)

 

存进同一个实例的情况(第一个版本)

我们通过一个具体的例子来说明 __new__ 方法在单例模式中的工作过程。

示例代码

我们将从头到尾展示每一步的执行过程,包括创建对象和调用方法的细节。

输出及解释

运行上述代码,输出将如下:

详细过程

  1. 第一次实例化 (p1 = Printer("Word app")):
      • 调用 __new__ 方法:
        • 输出 "调用 new 方法"。
        • cls.instanceNone,因此创建一个新的实例:
          • 输出 "创建一个新的实例"。
          • super().__new__(cls) 创建新实例,赋值给 obj
          • cls.instance 被设置为 obj
        • 返回 cls.instance
      • 调用 __init__ 方法:
        • 输出 "调用 init 方法"。
        • 初始化 name 为 "Word app"。
  1. 第二次实例化 (p2 = Printer("Excel app")):
      • 调用 __new__ 方法:
        • 输出 "调用 new 方法"。
        • cls.instance 不为 None,因此返回已有的实例:
          • 输出 "返回已有的实例"。
        • 返回 cls.instance
      • 调用 __init__ 方法:
        • 输出 "调用 init 方法"。
        • 初始化 name 为 "Excel app"(覆盖了之前的 "Word app")。
  1. 第三次实例化 (p3 = Printer("PPT app")):
      • 调用 __new__ 方法:
        • 输出 "调用 new 方法"。
        • cls.instance 不为 None,因此返回已有的实例:
          • 输出 "返回已有的实例"。
        • 返回 cls.instance
      • 调用 __init__ 方法:
        • 输出 "调用 init 方法"。
        • 初始化 name 为 "PPT app"(覆盖了之前的 "Excel app")。
  1. 添加任务:
      • p1.add_task("word file")
        • 输出 "Word app添加任务word file到打印机,总任务数量1"。
      • p2.add_task("excel file")
        • 输出 "Excel app添加任务excel file到打印机,总任务数量2"。
      • p3.add_task("ppt file")
        • 输出 "PPT app添加任务ppt file到打印机,总任务数量3"。
  1. 打印所有实例对象:
      • print("所有实例对象:", p1, p2, p3) 输出三个对象,确认它们是同一个实例:
        • 输出 <__main__.Printer object at 0x7f8b8465e820> <__main__.Printer object at 0x7f8b8465e820> <__main__.Printer object at 0x7f8b8465e820>
通过这个详细的例子,你可以看到 __new__ 方法如何确保只创建一个实例,而不论你实例化多少次这个类。所有实例共享同一个对象,并且任务列表也是共享的。
 

解释

代码解释

  1. 类定义:
      • Printer 类被定义。它有一个类变量 tasksinstancetasks 存储所有任务,instance 存储第一个实例对象。
  1. 初始化方法 __init__:
      • __init__ 方法是实例化对象时调用的构造方法,用于初始化实例变量。在这里,self.name 被初始化。
  1. 添加任务方法 add_task:
      • add_task 方法用于向任务列表中添加新任务,并输出打印机的名称、任务及总任务数量。
  1. 新的实例化方法 __new__:
      • __new__ 方法在对象实例化时首先被调用。它确保了 Printer 类只能有一个实例对象(单例模式)。
      • instanceNone 时,进行正常实例化,并将实例对象存储在 instance 中。
      • instance 不为 None 时,直接返回存储的实例对象。
  1. 实例化对象:
      • p1 = Printer("Word app")
      • p2 = Printer("Excel app")
      • p3 = Printer("PPT app")
      • 因为单例模式,这三个变量实际上都是同一个实例对象。
  1. 添加任务:
      • p1.add_task("word file")
      • p2.add_task("excel file")
      • p3.add_task("ppt file")
      • 每次调用 add_task 方法,任务被添加到同一个任务列表中,并输出当前任务信息。
  1. 打印对象:
      • print(p1, p2, p3) 输出实例对象的内存地址。因为单例模式,它们的内存地址相同。

输出

解释

  • obj <__main__.Printer object at 0x7f8b8465e820> 表示 Printer 类第一次实例化时创建了一个新的实例对象。
  • Word app添加任务word file到打印机,总任务数量1 表示 p1 添加了一个任务。
  • Excel app添加任务excel file到打印机,总任务数量2 表示 p2 添加了一个任务。
  • PPT app添加任务ppt file到打印机,总任务数量3 表示 p3 添加了一个任务。
  • 最后输出三个变量的内存地址,确认它们实际上都是同一个实例对象。
 
 

两个情况的区别

我们来比较一下这两个版本的 Printer 类。以下是第二个版本的代码:

主要区别

  1. 单例模式:
      • 第一个版本使用了单例模式。无论创建多少个 Printer 实例,最终只会有一个实例。这是通过 __new__ 方法实现的。如果已经存在实例对象,则返回这个实例,而不创建新的实例。
      • 第二个版本没有使用单例模式。每次实例化 Printer 类时,都会创建一个新的实例对象。
  1. 实例化对象:
      • 第一个版本中,p1p2p3 实际上都是同一个对象,因为它们都指向同一个实例。
      • 第二个版本中,p1p2p3 是三个独立的对象。

代码执行对比

第一个版本(单例模式):
  • p1 = Printer("Word app") 创建一个 Printer 实例并保存为 instance
  • p2 = Printer("Excel app") 返回已经存在的实例对象。
  • p3 = Printer("PPT app") 返回已经存在的实例对象。
  • 因为所有的实例都是同一个对象,调用 add_task 时,所有任务都添加到同一个任务列表中。
第二个版本:
  • p1 = Printer("Word app") 创建一个新的 Printer 实例。
  • p2 = Printer("Excel app") 创建一个新的 Printer 实例。
  • p3 = Printer("PPT app") 创建一个新的 Printer 实例。
  • 尽管每个实例的 tasks 列表是共享的(因为它是一个类变量),但实例本身是独立的。

输出对比

第一个版本(单例模式) 的输出:
第二个版本 的输出:

结论

  1. 单例模式的目的 是确保一个类只有一个实例,并且提供一个全局访问点。在第一个版本中,这通过 __new__ 方法实现。
  1. 第二个版本 创建了多个独立的 Printer 实例,但由于 tasks 是类变量,因此它们共享同一个任务列表。
 
 
 

打赏

如果您觉得我的内容对你有所帮助,不要吝啬你的一键三连!如果你有能力的话也可以通过下面请我喝杯咖啡~金额您随意~如果对文章内容有任何疑问,欢迎加入群组联系我~
notion image
上一篇
Python面向对象编程11—动态加载模块
下一篇
Python面向对象编程13—动态创建一个类