python 什么场景使用python的__new__魔法方法,以及__new__作用和原理
什么场景使用python的__new__魔法方法,以及__new__作用和原理
1. __new__
是什么
__new__
是python内置的魔术方法,并且是一个类方法,作用也很简单,就是在python的类进行实例化的时候,创建实例。并且__new__
的执行是一定在__init__
之前的
class T:
def __new__(cls, *args, **kwargs):
print("__new__ is executing")
return super().__new__(cls)
def __init__(self):
print("__init__ is executing")
self.a = 1
>> t = T()
__new__ is executing
__init__ is executing
所以可以在构造函数__init__
运行之前,可通过__new__
方法对实例的创建进行一些改造。
2. __new__
的使用
2.1 参数
cls
先来讲第一个参数,也是最重要的参数:
__new__
的第一个参数一定是会传递需要被实例化的类,这是在python底层就写死了的。通常来说,其约定俗成的参数名是cls
熟悉类方法修饰器的
@classmethod
一定也熟悉这个约定俗成的参数名,在类方法修饰器修饰的函数中,第一个参数一般也是cls
,代表类本身
所以,如果你要把super().__new__(cls)
中的cls
变量换成类名本身,当然也是没问题的;
class T:
def __new__(cls, *args, **kwargs):
print("__new__ is executing")
# 这里的 T 即是该类的类名,等同于这里的cls,即类本身
return super().__new__(T)
def __init__(self):
print("__init__ is executing")
self.a = 1
同样,因为cls
和self
一样,是约定俗成的写法,而非python的特殊保留字,而仅仅是代表了类和实例,所以把cls
的参数名换成其他乱七八糟的参数名称也是可以正常执行的,但是这样的写法不符合python代码的书写规范,极其不建议这样违背常规的写法。
class T:
def __new__(abc, *args, **kwargs):
"""
!!切勿模仿该参数的命名!!
”“”
print("__new__ is executing")
return super().__new__(abc)
def __init__(self):
print("__init__ is executing")
self.a = 1
如上2种写法的执行均可正常运行,如下:
>> t = T()
__new__ is executing
__init__ is executing
*args, **kwargs
这俩参数应该都很熟悉了,和传递到__init__
的参数是相同的,所以需保持一致
class T:
def __new__(cls, *args):
print("__new__ is executing")
print("__new__ args: " + str(args))
return super().__new__(cls)
def __init__(self, *args):
print("__init__ is executing")
print("__init__ args: " + str(args))
所以可以讲初始化参数在传递到__init__
前对类实例化的参数进行预处理。
>> t = T(1,2,3)
__new__ is executing
__new__ args: (1, 2, 3)
__init__ is executing
__init__ args: (1, 2, 3)
2.2 返回值
覆写的__new__
必须返回由object
的__new__
方法创建的实例
def __new__(cls, *args, **kwargs):
print("__new__ is executing")
return super().__new__(cls)
如上的super().__new__(cls)
,即是通过super()
调用父类object
的初始__new__
方法来创建实例。
但是如果__new__
方法没有返回实例,则该类不会执行__init__
,__new__
返回什么,该类就是什么;下面的例子返回的None
,就相当于是一个空类型,即None
本身(NoneType):
class T:
def __new__(cls, *args, **kwargs):
print("__new__ is executing")
# return super().__new__(cls)
return None
def __init__(self):
print("__init__ is executing")
self.a = 1
def whatever(self):
print(self.a)
print("done")
输出如下:
>> t = T()
__new__ is executing
>> type(t)
Out[20]: NoneType
3. __new__
的使用场景
__new__
有很多使用的场景,下面简单举2个例子:
单例模式
不清楚单例模式的盆友,可以看我的这篇文章 单例模式是什么以及如何创建单例模式
通过__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>}
这样就是单例模式了。
构建元类
元类是python里一切元素的基础,是构造类、修饰类的方法
元类的构建需要继承type
类,并且需要用__new__
方法去定义元类的构造。
这里针对元类的__new__
会和普通类的__new__
有所不同,这里不详细描述了,后面应该会专门开一篇讲元类。
class TestMeta(type):
def __new__(mcs, *args, **kwargs):
print(mcs.__dict__)
print(type(mcs))
return super().__new__(mcs, *args, **kwargs)
class T(object, metaclass=TestMeta):
def __new__(cls, *args, **kwargs):
print("new")
print(type(cls))
return super().__new__(cls)
def __init__(self):
print("init")
{'__module__': '__main__', '__new__': <staticmethod object at 0x000002912F510470>, '__doc__': None}
<class 'type'>
>> t = T()
new
<class '__main__.TestMeta'>
init