__init__ 和__new__区别?

真假构造函数

如果你去面试Python工程师的岗位,面试官问你,请问Python当中的类的构造函数是什么?

你不假思索,当然是__init__啦!如果你这么回答,很有可能你就和offer无缘了。因为在Python当中__init__并不是构造函数,__new__才是。是不是有点蒙,多西得(日语:为什么)?我们不是一直将__init__方法当做构造函数来用的吗?怎么又冒出来一个__new__,如果__new__才是构造函数,那么为什么我们创建类的时候从来不用它呢?

别着急,我们慢慢来看。首先我们回顾一下__init__的用法,我们随便写一段代码:

classStudent:def__init__(self,name,gender):self.name=name        self.gender=gender

1

2

3

4

我们一直都是这么用的,对不对,毫无问题。但是我们换一个问题,我们在Python当中怎么实现单例(Singleton)的设计模式呢?怎么样实现工厂呢?

从这个问题出发,你会发现只使用__init__函数是不可能完成的,因为__init__并不是构造函数,它只是初始化方法。也就是说在调用__init__之前,我们的实例就已经被创建好了,__init__只是为这个实例赋上了一些值。如果我们把创建实例的过程比喻成做一个蛋糕,__init__方法并不是烘焙蛋糕的,只是点缀蛋糕的。那么显然,在点缀之前必须先烘焙出一个蛋糕来才行,那么这个烘焙蛋糕的函数就是__new__。

__new__函数

我们来看下__new__这个函数的定义,我们在使用Python面向对象的时候,一般都不会重构这个函数,而是使用Python提供的默认构造函数,Python默认构造函数的逻辑大概是这样的:

def__new__(cls,*args,**kwargs):returnsuper().__new__(cls,*args,**kwargs)

1

2

从代码可以看得出来,函数当中基本上什么也没做,就原封不动地调用了父类的构造函数。这里隐藏着Python当中类的创建逻辑,是根据继承关系一级一级创建的。根据逻辑关系,我们可以知道,当我们创建一个实例的时候,实际上是先调用的__new__函数创建实例,然后再调用__init__对实例进行的初始化。我们可以简单做个实验:

classTest:def__new__(cls):print('__new__')returnobject().__new__(cls)def__init__(self):print('__init__')

1

2

3

4

5

6

当我们创建Test这个类的时候,通过输出的顺序就可以知道Python内部的调用顺序。

从结果上来看,和我们的推测完全一样。

单例模式

那么我们重写__new__函数可以做什么呢?一般都是用来完成__init__无法完成的事情,比如前面说的单例模式,通过__new__函数就可以实现。我们来简单实现一下:

当然,如果是在并发场景当中使用,还需要加上线程锁防止并发问题,但逻辑是一样的。

除了可以实现一些功能之外,还可以控制实例的创建。因为Python当中是先调用的__new__再调用的__init__,所以如果当调用__new__的时候返回了None,那么最后得到的结果也是None。通过这个特性,我们可以控制类的创建。比如设置条件,只有在满足条件的时候才能正确创建实例,否则会返回一个None。

比如我们想要创建一个类,它是一个int,但是不能为0值,我们就可以利用__new__的这个特性来实现:

classNonZero(int):def__new__(cls,value):returnsuper().__new__(cls,value)ifvalue!=0elseNone

1

2

3

那么当我们用0值来创建它的时候就会得到一个None,而不是一个实例。

总结

共同点:

(1) __init__和__new__都是python类中的内置方法

(2) __init__和__new__都会在创建对象时自动被调用

不同点:

(1) 作用

__new__创建实例

__init__初始化实例

(2) 运行时间

__new__方法在__init__方法之前被调用

(3) 属于类属性还是实例属性(python中属性和方法都称作属性)

__new__是类方法,也就是类属性

__init__是实例方法,也就是实例属性

(4) 参数

__new__参数cls–当前类,调用时需手动绑定(也就是手动传参)将其绑定到cls类上。如:super(A, cls).__new__(cls),其中super(A,cls)可以写为super()。

__init__参数self–实例化的对象,调用时解释器会自动绑定(也就是解释器会自动将调用对象obj传递给__init__的第一个参数self)将其绑定到self实例对象上。如: super(A, self).__init__(),其中super(A,self)可以写为super()。

绑定:可以在所调用的方法中使用所绑定对象的某些属性

辨析super(A,cls)和super(A, self)

(5) 可使用属性

__new__可通过cls使用类属性

__init__可通过self使用实例属性和类属性

(6) 返回值

__new__必须有返回值。返回值为类本身实例时,会将返回的类本身实例传递给__init__的第一个参数self,并运行__init__对实例进行初始化;返回值为其它类实例时,不会运行__init__方法。

__init__没有返回值

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容