__init__和__new__区别?
真假构造函数
如果你去⾯试Python⼯程师的岗位,⾯试官问你,请问Python当中的类的构造函数是什么?
python单例模式你不假思索,当然是__init__啦!如果你这么回答,很有可能你就和offer⽆缘了。因为在Python当中__init__并不是构造函数,__new__才是。是不是有点蒙,多西得(⽇语:为什么)?我们不是⼀直将__init__⽅法当做构造函数来⽤的吗?怎么⼜冒出来⼀个__new__,如果__new__才是构造函数,那么为什么我们创建类的时候从来不⽤它呢?
别着急,我们慢慢来看。⾸先我们回顾⼀下__init__的⽤法,我们随便写⼀段代码:
classStudent:def__init__(self,name,gender):self.name=name der=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__没有返回值
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论