Python单例模式【详细】:(概念、__new__方法概念与重写、单例设计模式应用场景、代码实现、只执行一次初始化动作)

导读:本篇文章讲解 Python单例模式【详细】:(概念、__new__方法概念与重写、单例设计模式应用场景、代码实现、只执行一次初始化动作),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

欢迎关注博主 python老鸟 或 前往 『Python自学网』, 从基础入门免费课程开始,逐步深入学习python全栈体系课程,适合新手入门到精通全栈开发。


免费专栏传送门:《Python基础教程

 


目录

一、设计模式和单例设计模式概念

1)设计模式:

2)单例设计模式:

3)单例设计模式应用场景

二、__new__方法

1)使用类名()创建对象时,Python解释器做的2件事:

2)重写__new__方法的代码非常固定:

3)重写__new__方法代码演练

三、Python中的单例

1)单例设计模式思路分析

2)实现单例设计模式——验证是否是同一个对象

四、只执行一次初始化方法


 

一、设计模式和单例设计模式概念

1)设计模式:

含义:

设计模式是前人工作的总结和提炼,通常被人们广泛的设计模式都是针对某一特定的问题的成熟的解决方案。

作用:

使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码的可靠性。

 

2)单例设计模式:

目的:

  1. 让类创建的对象在系统中只有唯一的一个实例
  2. 每一次执行 类名() 返回的对象,内存地址是相同的

 

3)单例设计模式应用场景

场景:

音乐播放对象(每次播放只能播放一首歌曲)、

回收站对象(电脑中只有一个回收站)、

……

共同特点:

这两个对象都只有唯一的存在

 


 

二、__new__方法

__new__方法(new字母前后分别是两个英文状态下的下划线,用两个下划线开头两个下划线结尾的方法是内置方法)

 

1)使用类名()创建对象时,Python解释器做的2件事:

  1. 使用 类名() 创建对象时,Python解释器首先会调用__new__方法为对象分配空间
  2. Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法

 

简单理解就是: 第一件事是为对象分配空间,第二件事是给对象初始化。

 

31b4dd1e826347709914408da1a9ade8.png

 

 

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

主要作用有2个:

  1. 在内存中为对象分配空间
  2. 返回对象的引用

 

2)重写__new__方法的代码非常固定:

  1. 重写__new__方法一定要返回分配了空间return super().__new__(cls)
  2. 否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法

注意:

__new__是一个静态方法,在调用时需要主动传递cls参数

 

3)重写__new__方法代码演练

简单代码:

class MusicPlayer(object):

    def __init__(self):
        print("播放器对象初始化")


# 创建播放器对象
player = MusicPlayer()

print(player)

执行结果:

fdcbb4a425654a73bfebfc5dc310725c.png

 

重写object基类提供的__new__方法代码1:

class MusicPlayer(object):
    def __new__(cls, *args, **kwargs):

        # 1. 创建对象时,new方法会被自动调用
        print("创建对象自动调用new方法,分配空间")

    def __init__(self):
        print("播放器对象初始化")


# 创建播放器对象
player = MusicPlayer()

print(player)

 

执行结果:

722c65321d9b441ca02cbecad929ea95.png

 

None:因为重写__new__方法没有返回分配了空间return super().__new__(cls),此时Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法。

 

重写object基类提供的__new__方法代码2(完整):

class MusicPlayer(object):
    def __new__(cls, *args, **kwargs):

        # 1. 创建对象时,new方法会被自动调用
        print("创建对象自动调用new方法,分配空间")

        # 2. 为对象分配空间
        # super():父类方法
        instance = super().__new__(cls)

        # 3. 返回对象的引用
        return instance

    def __init__(self):
        print("播放器对象初始化")


# 创建播放器对象
player = MusicPlayer()

print(player)

执行结果:

6136d93687bf4a929781df987f217f49.png

 


 

三、Python中的单例

1)单例设计模式思路分析

单例 —— 让类创建的对象,在系统中只有唯一的一个实例(也就是使用这个类无论创建多少次对象都是同一个对象)

 

思路分析:

  1. 定义同一个类属性,初始值是None用于记录单例对象的引用
  2. 重写__new__方法
  3. 如果类属性is None调用父类方法分配空间,并在类属性中记录结果
  4. 返回类属性中记录的对象引用

4f7a22c3a03d4f90b09f17d20d805aaf.png

 

 

2)实现单例设计模式——验证是否是同一个对象

1.验证前准备,确定此时不是同一个对象:

 

代码:

class MusicPlayer(object):
    pass


# 创建多个对象,对比地址是否相同
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

 

执行结果:地址不同

387b782d6c4444aea6afbc8c248fc56f.png

2.验证单例设计模式是不是同一个对象:

代码:

class MusicPlayer(object):
    # 记录第一个被创建对象的引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否是一个空对象,没有是空对象,说明第一个对象还没被创建
        if cls.instance is None:
            # 2. 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)

        # 3. 返回类属性保存的对象引用
        return cls.instance


# 创建多个对象,对比地址是否相同
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

执行结果:地址相同,证明这两个对相关本质上是相同对象

68aa89ec6c2e46479469a3d9372ab687.png

 


 

四、只执行一次初始化方法

 

在每次使用类名() 创建对象时,Python解释器都会自动调用者两个方法:

  1. __new__  分配空间
  2. __init__  对象初始化

在上面的代码对__new__方法改造之后,每次都会得到第一次被创建对象的引用,但是初始化方法还是会被再次调用。

——————————————————————————————————–

 

需求:让初始化动作只被执行一次。

解决办法:

  1. 定义一个类属性init_flag标记是否执行过初始化动作,初始值为False
  2. 在__init__方法中判断init_flag,如果为False就执行初始化动作
  3. 然后将init_flag设置为True,这样再次调用__init__方法时,初始化动作就不会被再次执行了

 

1.代码实现前准备,创建了两次对象,初始化方法被执行两次:如图

e6a9ef8746574d88a07e1b5a1b8b9a33.png

2.初始化动作只被执行一次的代码:

class MusicPlayer(object):
    # 记录第一个被创建对象的引用
    instance = None
    # 记录是否执行初始化动作
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否是一个空对象,没有是空对象,说明第一个对象还没被创建
        if cls.instance is None:
            # 2. 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)

        # 3. 返回类属性保存的对象引用
        return cls.instance

    def __init__(self):

        # 1. 判断是否执行过初始化动作
        if MusicPlayer.init_flag:
            return
        # 2. 如果没有被执行过,再执行初始化动作
        print("初始化方法执行")

        # 3. 修改类属性init_flag的值为True
        MusicPlayer.init_flag = True


# 创建多个对象,对比地址是否相同
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

增加代码:

47658a1462704b048b5ba04275783700.png

 

执行结果:

fa9c1b18fc79429d9616a0f1823a0b0d.png

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/73313.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!