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

 

相关文章
|
4天前
|
Python
【Python进阶(三)】——面向对象编程
【Python进阶(三)】——面向对象编程
|
4天前
|
算法 Java 程序员
[重学Python] Day6 面向对象编程 基础
面向对象编程基础讲解,包括类与对象的概念,类是对象的模板,对象是类的实例。Python中使用`class`定义类,通过`__init__`初始化对象。创建对象并调用方法如`drive`和`target_client`。访问权限在Python中相对宽松,使用单下划线表示受保护的属性。面向对象的三大支柱是封装、继承和多态,封装是隐藏实现细节,仅暴露简单接口。提供了数字时钟和平面上的点的类定义作为练习示例。
14 0
|
4天前
|
运维 算法 Shell
第六章 Python类(面向对象编程)
第六章 Python类(面向对象编程)
|
4天前
|
Python
Python从入门到精通:深入学习面向对象编程——2.1.2继承、封装和多态的概念
Python从入门到精通:深入学习面向对象编程——2.1.2继承、封装和多态的概念
|
4天前
|
设计模式 算法 程序员
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
|
4天前
|
数据安全/隐私保护 Python
Python从入门到精通——2.2.1深入学习面向对象编程:类和对象的定义
Python从入门到精通——2.2.1深入学习面向对象编程:类和对象的定义
|
4天前
|
存储 数据安全/隐私保护 Python
《Python 简易速速上手小册》第4章:Python 面向对象编程(2024 最新版)
《Python 简易速速上手小册》第4章:Python 面向对象编程(2024 最新版)
30 0
|
4天前
|
程序员 Python
面向对象编程(OOP)在Python中的应用
【4月更文挑战第8天】面向对象编程(OOP)是使用对象设计软件的编程范式,Python支持OOP,提供类、对象、继承和多态等概念。类是创建对象的蓝图,包含属性和方法。通过`class`关键字定义类,如`Person`类,然后通过类创建对象。继承使子类继承父类属性和方法,如`Student`继承`Person`。多态允许不同类的对象通过相同接口操作,如`print_greeting`函数调用不同对象的`greet`方法。掌握这些概念能编写高效、可维护的Python代码。
|
4天前
|
Python
面向对象编程——Python中的类
面向对象编程——Python中的类
|
4天前
|
Python
Python面向对象编程学习应用案例详解
面向对象编程在Python中通过类定义对象结构和行为。示例:1) 使用`class`关键字定义类,如`class Person`;2) `__init__`方法初始化对象属性,如`self.name`和`self.age`;3) 实例化对象,如`person1 = Person("张三", 25)`;4) 访问属性和方法,如`person1.name`;5) 定义类方法,如`def introduce(self)`;6) 调用方法,如`person1.introduce()`;7) 类继承,如`class Student(Person)`;8) 多态,通过继承重写方法实现。
10 1