单例模式

该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。比如:

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

new方法

  • 使用类名()创建对象时,Python解释器首先会调用__new__方法为对象分配空间
  • __new__是一个由object基类提供的内置的静态方法,主要作用有两个:
    1 在内存中为对象分配空间
    2 返回对象的引用
class MusicPlayer(object):

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

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

print(player)
输出:
播放器初始化
<__main__.MusicPlayer object at 0x0000020B7CC0A908>

可以看到在我们创建对象后,内存中有一块空间存放我们的对象。接下来我们在代码中加上__new__方法。

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

        # 1.创建对象时,new方法会被自动调用
        print("创建对象,分配空间")
       
    def __init__(self):
        print("播放器初始化")

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

print(player)
输出:
创建对象,分配空间
None

???为什么内存地址为空了?并且__init__也没有执行?,其实重写__new__方法非常固定,一定要 return super().new(cls), 否则Python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法,并且还要注意:__new__是一个静态方法,在调用时需要主动传递 cls 参数

class MusicPlayer(object):


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

        # 1.创建对象时,new方法会被自动调用
        print("创建对象,分配空间")
        # 2.为对象分配空间
        return super().__new__(cls)
        # 3.返回对象的引用

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

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

print(player)
输出:
创建对象,分配空间
播放器初始化
<__main__.MusicPlayer object at 0x000002589EFCB668>

单例演练

class MusicPlayer(object):
    pass

player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

输出:
<__main__.MusicPlayer object at 0x000001E65983A908>
<__main__.MusicPlayer object at 0x000001E659847EB8>

我们创建了一个音乐播放器的类,在两次调用中,可以看到有两个不同的内存地址,如果是单例模式那么应该是我们不管使用类名()创建多少个对象,都只有一个内存地址,我们改造一下。

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
    pass

player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

输出:
<__main__.MusicPlayer object at 0x000001A44D55B588>
<__main__.MusicPlayer object at 0x000001A44D55B588>

我们创建一个类属性,来记录第一个类创建时候的引用,然后再有创建的话就不会重新分配内存地址了。

单例的初始化只执行一次

在上面的代码中我们确实只有一个实例了,但是初始化方法是否只调用了一次呢?

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

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



player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

输出:
初始化播放器
<__main__.MusicPlayer object at 0x000002559993BA58>
初始化播放器
<__main__.MusicPlayer object at 0x000002559993BA58>

在两次创建实例时,都调用了初始化方法,在实例化过程中,我们无法阻止初始化方法的执行,但是可以通过增加一个标记达到只执行一次。

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.修改类属性标记
        MusicPlayer.init_flag = True


player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)
初始化播放器
<__main__.MusicPlayer object at 0x0000012EAE31B550>
<__main__.MusicPlayer object at 0x0000012EAE31B550>

我们用init_flag这个类属性去记录是否执行过初始化方法,默认False,当执行过初始化动作后,我们就把状态改为True,保证下一次创建是不再去执行初始化动作一遍。


继续保持学习,在于寒冬之中亦有光芒散放。