【Python设计模式】new方法与单例模式

在网上看了很多关于__new__()魔法方法的教程,毫无疑问大部分都是单例模式相关,确实如此,利用__new__()魔法方法的特性,在Python中很容易实现单例模式。
在面向对象编程的语言中,很多语言都有一个构造函数,例如JavaC++,在Python中也存在一个构造函数__init__(),不过在python中叫做魔法方法,在类实例化的时候初始化类成员变量。运行下面的程序将会打印init字符串。

class test:
    def __init__(self):
        print('init')

if __name__ == '__main__':
    a = test()
    
#程序输出
#init

刚开始学习Python的时候我觉得在类实例化的过程中第一运行的就是__init__()方法,后来发现还有一个比它先运行的魔法方法__new__()方法,运行下面的代码可以看到确实如此。

class test:
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new')

if __name__ == '__main__':
    a = test()

#程序输出
#new

但是。。。 为什么__init__()方法没有执行,程序运行后并没有打印init字符串,按照__new__()方法先被执行__init__()方法应该在它执行之后执行,但是并没有,因为我们把__new__()方法给重写覆盖了,失去了它原来的属性,因此我们在重写__new__()方法的时候需要继承它原来的属性。从下面的代码中可以看出在进行__init__()初始化的时候先执行了__new__()方法,也就是说__new__()__init__()共同构成了类的实例化时的类成员变量的初始化。

class test:
    def __init__(self):
        print('init')

    def __new__(cls, *args, **kwargs):
        print('new')
        _super = super().__new__(cls, *args, **kwargs)
        return _superdizhi

if __name__ == '__main__':
    a = test()

#程序输出
#new
#init

_super返回对象的实例地址,下面的代码打印了两次对象的实例地址,通过分析可以知道当我们打印一个实例化的类对象时,其实打印的就是__new__()魔法方法的返回值,在下面的代码中,我们重写了__new__()方法并且保留了其原来的属性,可以看出_super即是类对象实例化后的地址。

class test:
    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        _super = super().__new__(cls, *args, **kwargs)
        print(_super)
        return _super

if __name__ == '__main__':
    a = test()
    print(a)

#程序输出
#<__main__.test object at 0x7fd8222bec50>
#<__main__.test object at 0x7fd8222bec50>

弄明白了__new__()魔法方法后想要实现单例模式并不难,我们只需要在__new__()方法中动动手脚就好了。下面的代码实现了每次创建test类对象时创建的都是同一个对象,内存地址没有改变。实现了基本的单例模式,但是test字符串被打印了两次,虽然创建的都是同一个对象,但是每次创建都会被初始化化一次,这其中所花费的系统开销是我们无法接受的,既然每次都是使用同一个实例,那为何还要重新初始化,浪费系统性能呢。

class test:
    def __init__(self):
        print('test')

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_super'):
            cls._super = super().__new__(cls, *args, **kwargs)
        return cls._super

if __name__ == '__main__':
    a = test()
    b = test()
    print(a)
    print(b)

#test
#test
#<__main__.test object at 0x7f1eb6c3d588>
#<__main__.test object at 0x7f1eb6c3d588>

为了节约系统开销,不让程序每次实例化都进行初始化,可以用一个标志位来控制__init__()的初始化,完成单例模式的设计。

class test:
    _super = None
    _init_flag = True

    def __init__(self):
        if self._init_flag:
            print('test')
            self._init_flag = False

    def __new__(cls, *args, **kwargs):
        if not cls._super:
            cls._super = super().__new__(cls, *args, **kwargs)
        return cls._super

if __name__ == '__main__':
    a = test()
    b = test()
    print(a)
    print(b)
    c = test()
    print(c)

#程序输出
#test
#<__main__.test object at 0x7f39ebbd0550>
#<__main__.test object at 0x7f39ebbd0550>
#<__main__.test object at 0x7f39ebbd0550>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值