python 单例模式是什么以及如何创建单例模式

1. 何为单例模式

首先解释一下什么是单例模式。

类就像是一台打印机,实例对象就像是由这个打印机打印出来的一份份文件,看起来好像每份文件都一样的,但实际上会由于每份文件的纸张不同等一系列差别,每份文件还是不一样的。

也就是说:
看起来好像由同一个类创建的多个实例是一样的,功能也一样,但实际上是存储在内存上不同位置的对象。

如下所示,写一个最简单的类,分别进行实例化:

class T:
    def __init__(self, a):
        self.a = a

    def test(self):
        print(self.a)
>> t1 = T(2)
>> t2 = T(2)
>> t1.test()
2
>> t2.test()
2
>> t1 == t2
False
>> id(t1)
2822587799984
>> id(t2)
2822587506872

如上可看到,即使是由同一个类,创建参数相同的实例,其内存地址的 id 也是不同的。

如果不明白python赋值和引用等原理的,可以看到的这篇文章 变量的引用赋值及深浅拷贝

所以如果在某些场景下,某个类会创建很多个实例,并且实现的方法也是一样的,这样会浪费很多内存空间。

如果不明白类和实例的关系的话,可以看我的这篇文章 一张图解释清楚何为“python一切皆为对象”

2. 通过__new__构造单例模式

所以可以通过__new__方法,以及通过一个类变量,控制类的实例化,保证参数相同的实例,有且仅有1个实例对象,减少内存空间:


class T:
    INSTANCE_DICT = {}

    def __new__(cls, *args):
        if cls.INSTANCE_DICT.get(str(args), None):
            print(f"已存在已创建的参数为{args}的实例,直接返回该实例")
            return cls.INSTANCE_DICT[str(args)]
        else:
            print(f"还未创建过参数为{args}的实例,新建一个实例")
            cls.INSTANCE_DICT[str(args)] = super().__new__(cls)
            return cls.INSTANCE_DICT[str(args)]

    def __init__(self, a):
        self.a = a

    def test(self):
        print(self.a)

运行如下:

>> t1 = T(1)
还未创建过参数为(1,)的实例,新建一个实例
>> t3 = T(1)
已存在已创建的参数为(1,)的实例,直接返回该实例

>> t2 = T(2)
还未创建过参数为(2,)的实例,新建一个实例
>> t4 = T(2)
已存在已创建的参数为(2,)的实例,直接返回该实例

>> t1 == t2
False

>> t1 == t3
True

>> t4 == t2
True

>> t1.INSTANCE_DICT
{'(1,)': <__main__.T at 0x2912f5fdbe0>, '(2,)': <__main__.T at 0x2912f5fd860>}

这样就是单例模式了。

3. 通过元类meta构造单例模式


class TestMeta(type):
    def __init__(cls, *args, **kwargs):
        print("meta init")
        cls.INSTANCE_DICT = {}
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print("meta call")
        if cls.INSTANCE_DICT.get(str(args), None):
            print(f"已存在已创建的参数为{args}的实例,直接返回该实例")
            return cls.INSTANCE_DICT[str(args)]
        else:
            print(f"还未创建过参数为{args}的实例,新建一个实例")
            obj = super().__call__(*args, **kwargs)
            cls.INSTANCE_DICT[str(args)] = obj
            return obj


class T(object, metaclass=TestMeta):
    def __new__(cls, *args, **kwargs):
        print("new")
        print(type(cls))
        return super().__new__(cls)

    def __init__(self, *args):
        print("init")
        print(args)
>> t1 = T(1)
meta call
还未创建过参数为(1,)的实例,新建一个实例
new
<class '__main__.TestMeta'>
init
(1,)
>> t2 = T(2)
meta call
还未创建过参数为(2,)的实例,新建一个实例
new
<class '__main__.TestMeta'>
init
(2,)
>> T.INSTANCE_DICT
{'(1,)': <__main__.T at 0x2ddd191a080>, '(2,)': <__main__.T at 0x2ddd19118d0>}
>> t3 = T(1)
meta call
已存在已创建的参数为(1,)的实例,直接返回该实例
>> T.INSTANCE_DICT
{'(1,)': <__main__.T at 0x2ddd191a080>, '(2,)': <__main__.T at 0x2ddd19118d0>}
>> t1 == t3
True
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值