目标:

  1. 单例设计模式
  2. __new__方法
  3. Python中的单例

1.单例设计模式

  • 设计模式:
    • 设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式,都是针对 某一特定问题 的成熟的解决方案
    • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
  • 单例设计模式
    • 目的——让 创建的对象,在系统中只有 唯一的一个实例
    • 每一次执行 类名() 返回的对象,内存地址是相同的
  • 单例设计模式的应用场景
    • 音乐播放 对象
    • 回收站 对象
    • 打印机 对象
    • ········

2.__new__方法

  • 使用 类名() 创建对象时,Python解释器会先调用__new__方法为对象分配空间,再调用__init__()方法初始化对象
  • __new__是由object基类提供的内置的静态方法,主要作用有两个
    1. 在内存中为对象分配空间
    2. 返回对象的引用
  • Python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
  •  1 class MusicPlayer(object):
     2     def __new__(cls, *args, **kwargs):
     3         print("创建对象,分配空间")
     4 
     5     def __init__(self):
     6         print("初始化对象")
     7 
     8 player = MusicPlayer()
     9 print(player)
    10 '''
    11 运行结果:
    12 创建对象,分配空间  :可以看出在实例化对象时,先调用了new方法为对象开辟空间
    13 None    :因为重写了new方法,而new方法需要将对象的地址传递给init,
    14           重写时并没有做,所以没有进行初始化
    15 '''
    View Code

     

  • 重写__new__方法的代码非常的固定
    1. 一定要  return super().__new__(cls)
    2. 否则Python解释器得不到分配了空间的引用,就不会调用对象的初始化方法
    3. 注意:__new__是一个静态方法,在调用时需要主动传递cls参数
    • class MusicPlayer(object):
          def __new__(cls, *args, **kwargs):
              #创建对象时,new方法会自动被调用
              print("创建对象,分配空间")
              #为对象分配空间
              instance = super().__new__(cls)
              #返回对象的引用
              return instance
          def __init__(self):
              print("初始化对象")
      
      player = MusicPlayer()
      print(player)
      '''
      运行结果:
      创建对象,分配空间
      初始化对象
      <__main__.MusicPlayer object at 0x000001DC63B9FF10>
      '''

       

    • 单例——让类创建的对象,在系统中只有唯一的一个实例
      1. 定义一个 类属性,初始值是None,用于记录单例对象的引用
      2. 重写new方法
      3. 如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
      4. 返回类属性中记录的对象引用
    •  1 #单例设计模式
       2 class MusicPlayer(object):
       3     instance = None     #记录对象的地址
       4     def __new__(cls, *args, **kwargs):
       5         #判断对象是否为空,如果为空,就为其分配地址
       6         if cls.instance is None:
       7             #为对象分配空间
       8             cls.instance = super().__new__(cls)
       9             #返回对象的引用
      10             return cls.instance
      11         #如果部位空,就返回第一个对象的地址
      12         else:
      13             return cls.instance
      14     def __init__(self):
      15         pass
      16 
      17 player_1 = MusicPlayer()    #<__main__.MusicPlayer object at 0x00000282C8B7FDF0>
      18 print(player_1)
      19 
      20 player_2 = MusicPlayer()    #<__main__.MusicPlayer object at 0x00000282C8B7FDF0>
      21 print(player_2)
      单例设计模式案例

 

  • 只执行一次初始化动作
    • 需求:让初始化动作只被执行一次
    • 解决办法
      1. 定义一个类属性init_flag标记是否 执行过初始化动作,初始值为False
      2. 在 __init__ 方法中,判断init_flag,如果为False就执行初始化动作
      3. 然后将init_flag设置为True
      4. 这样,再次调用__init__方法时,初始化动作就不会被执行了
    •  1 #初始化动作只执行一次
       2 class MusicPlayer(object):
       3     instance = None     #记录对象的地址
       4     init_flag = False   #标记是否执行过初始化动作
       5     def __new__(cls, *args, **kwargs):
       6         #判断对象是否为空,如果为空,就为其分配地址
       7         if cls.instance is None:
       8             #为对象分配空间
       9             cls.instance = super().__new__(cls)
      10             #返回对象的引用
      11             return cls.instance
      12         #如果部位空,就返回第一个对象的地址
      13         else:
      14             return cls.instance
      15     def __init__(self):
      16         #判断初始化动作是否执行过
      17         if MusicPlayer.init_flag:
      18             return
      19         #如果没有执行过,那么执行初始化动作
      20         print("初始化播放器")
      21         #修改类属性(init_flag)的标记
      22         MusicPlayer.init_flag = True
      23 
      24 player_1 = MusicPlayer()    #<__main__.MusicPlayer object at 0x00000282C8B7FDF0>
      25 print(player_1)
      26 player_2 = MusicPlayer()    #<__main__.MusicPlayer object at 0x00000282C8B7FDF0>
      27 print(player_2)
      28 player_3 = MusicPlayer()    #<__main__.MusicPlayer object at 0x00000282C8B7FDF0>
      29 print(player_3)
      30 '''
      31 运行结果:
      32 初始化播放器
      33 <__main__.MusicPlayer object at 0x0000029ECB252F10>
      34 <__main__.MusicPlayer object at 0x0000029ECB252F10>
      35 <__main__.MusicPlayer object at 0x0000029ECB252F10>
      36 '''
      只执行一次初始化动作
"""优化后的单例设计模式"""
import threading
class MusicPlayer(object):
    # 设置标记
    instance = None
    init_flag = None
    # 创建锁对象
    lock = threading.RLock()
    # 构造方法
    def __new__(cls, *args, **kwargs):
        if cls.instance:
            return cls.instance

        with cls.lock:
            if not cls.instance:
                cls.instance = object.__new__(cls)
            return cls.instance
    # 初始化方法,不过初始化方法通常不需要只初始化一次下面是演示如果需要初始化一次代码应该怎么写
    def __init__(self, name):
        # if cls.init_flag       这样子写代码会报错,cls只能在classmethod和new中才能获取到
        if MusicPlayer.init_flag:
            return
        with self.lock:
            if not MusicPlayer.init_flag:
                self.name = name
                MusicPlayer.init_flag = True

a = MusicPlayer("nihao1")
b = MusicPlayer("nihao2")
c = MusicPlayer("nihao9")
print(a.name, a)
print(b.name, b)
print(c.name, c)

 

posted on 2021-10-26 20:01  J.FengS  阅读(122)  评论(0编辑  收藏  举报