在网上看了很多关于__new__()
魔法方法的教程,毫无疑问大部分都是单例模式相关,确实如此,利用__new__()
魔法方法的特性,在Python
中很容易实现单例模式。
在面向对象编程的语言中,很多语言都有一个构造函数,例如Java
和C++
,在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>