Python - 面向对象编程 - __new__() 和单例模式

简介: Python - 面向对象编程 - __new__() 和单例模式

单例模式


这是一种设计模式

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

 

单例设计模式

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

 

单例设计模式的应用场景

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

 

为什么要单例模式?

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

 

__new__ 方法


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

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


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

 

内置的静态方法

image.png

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

 

__new__ 主要作用

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

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


image.png


重写 __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. 如果类属性已有对象引用,则直接返回


image.png

单例模式的代码实现

# 单例模式
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()
# 输出结果
初始化动作

 

相关文章
|
5月前
|
Python
Python 中__new__方法详解及使用
__new__ 是 Python 中用于创建类实例的静态方法,在实例化对象时优先于 __init__ 执行。它定义在基础类 object 中,需传递 cls 参数(表示当前类)。__new__ 可决定是否使用 __init__ 方法或返回其他对象作为实例。特性包括:1) 在实例化前调用;2) 始终为静态方法。示例中展示了其用法及 Python2 和 Python3 的差异,强调了参数处理的不同。
198 10
|
5月前
|
Python
Python 中__new__方法详解及使用
`__new__` 是 Python 中的一个特殊方法,用于控制对象的创建过程,在 `__init__` 之前执行。它是类的静态方法,负责返回一个实例。如果 `__new__` 不返回对象,`__init__` 将不会被调用。本文详细介绍了 `__new__` 的作用、特性及与 `__init__` 的区别,并通过实例演示了其在单例模式中的应用,同时对比了 Python2 和 Python3 中的写法差异。
179 0
|
9月前
|
存储 IDE Shell
Python单例模式中的问题
本文介绍了Python中几种常见的单例模式实现方式及其优缺点。首先,装饰器形式的单例模式通过包装类为函数来确保单例,但存在无法使用`isinstance()`和联合类型符号`|`的问题。其次,元类形式的单例模式通过自定义元类来实现单例,解决了装饰器模式的缺陷,但在继承同样使用元类的类时可能会遇到冲突。最后,模块级单例模式和类属性单例模式虽然简单直接,但不具备通用性,需要针对每种类型单独实现。总结来看,元类形式的单例模式相对较为理想,尽管可能需要打补丁,但对用户透明且不影响客户端代码。作者:三叔木卯,来源:稀土掘金。
109 12
|
12月前
|
Java C# Python
Python学习七:面向对象编程(中)
这篇文章是关于Python面向对象编程的中级教程,涵盖了析构函数、对象的三大特征(封装、继承、多态)、类属性与实例属性、以及类方法与静态方法的对比。
99 2
|
12月前
|
设计模式 安全 JavaScript
Python学习八:面向对象编程(下):异常、私有等
这篇文章详细介绍了Python面向对象编程中的私有属性、私有方法、异常处理及动态添加属性和方法等关键概念。
114 1
|
12月前
|
Python
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
深入解析 Python 中的对象创建与初始化:__new__ 与 __init__ 方法
119 1
|
存储 Java 程序员
30天拿下Python之面向对象编程
30天拿下Python之面向对象编程
57 3
|
Java Python
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
【9月更文挑战第18天】在 Python 中,虽无明确的 `interface` 关键字,但可通过约定实现类似功能。接口主要规定了需实现的方法,不提供具体实现。抽象基类(ABC)则通过 `@abstractmethod` 装饰器定义抽象方法,子类必须实现这些方法。使用抽象基类可使继承结构更清晰、规范,并确保子类遵循指定的方法实现。然而,其使用应根据实际需求决定,避免过度设计导致代码复杂。
123 6
|
Python
全网最适合入门的面向对象编程教程:Python函数方法与接口-函数与方法的区别和lamda匿名函数
【9月更文挑战第15天】在 Python 中,函数与方法有所区别:函数是独立的代码块,可通过函数名直接调用,不依赖特定类或对象;方法则是与类或对象关联的函数,通常在类内部定义并通过对象调用。Lambda 函数是一种简洁的匿名函数定义方式,常用于简单的操作或作为其他函数的参数。根据需求,可选择使用函数、方法或 lambda 函数来实现代码逻辑。
139 7
|
12月前
|
设计模式 存储 数据库连接
Python编程中的设计模式之美:单例模式的妙用与实现###
本文将深入浅出地探讨Python编程中的一种重要设计模式——单例模式。通过生动的比喻、清晰的逻辑和实用的代码示例,让读者轻松理解单例模式的核心概念、应用场景及如何在Python中高效实现。无论是初学者还是有经验的开发者,都能从中获得启发,提升对设计模式的理解和应用能力。 ###

推荐镜像

更多