千锋教育-做有情怀、有良心、有品质的职业教育机构

Python之__init__和__new__的区别是什么

来源:千锋教育
发布时间:2023-11-06 14:38:58
分享

真假构造函数

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

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

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

classStudent:

def__init__(self,name,gender):

self.name=name

self.gender=gender

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

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

__new__函数

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

def__new__(cls,*args,**kwargs):

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

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

classTest:

def__new__(cls):

print('__new__')

returnobject().__new__(cls)

def__init__(self):

print('__init__')

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

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

单例模式

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

classSingletonObject:

def__new__(cls,*args,**kwargs):

ifnothasattr(SingletonObject,"_instance"):

SingletonObject._instance=object.__new__(cls)

returnSingletonObject._instance

def__init__(self):

pass

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

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

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

classNonZero(int):

def__new__(cls,value):

returnsuper().__new__(cls,value)ifvalue!=0elseNone

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

工厂模式

理解了__new__函数的特性之后,我们就可以灵活运用了。我们可以用它来实现许多其他的设计模式,比如大名鼎鼎经常使用的工厂模式。

所谓的工厂模式是指通过一个接口,根据参数的取值来创建不同的实例。创建过程的逻辑对外封闭,用户不必关系实现的逻辑。就好比一个工厂可以生产多种零件,用户并不关心生产的过程,只需要告知需要零件的种类。也因此称为工厂模式。

比如说我们来创建一系列游戏的类:

classLast_of_us:

defplay(self):

print('theLastOfUsisreallyfunny')

classUncharted:

defplay(self):

print('theUnchartedisreallyfunny')

classPSGame:

defplay(self):

print('PShasmanygames')

然后这个时候我们希望可以通过一个接口根据参数的不同返回不同的游戏,如果不通过__new__,这段逻辑就只能写成函数而不能通过面向对象来实现。通过重载__new__我们就可以很方便地用参数来获取不同类的实例:

classGameFactory:

games={'last_of_us':Last_Of_us,'uncharted':Uncharted}

def__new__(cls,name):

ifnameincls.games:

returncls.games[name]()

else:

returnPSGame()

uncharted=GameFactory('uncharted')

last_of_us=GameFactory('last_of_us')

总结

相信看到这里,关于__new__这个函数的用法应该都能理解了。一般情况下我们是用不到这个函数的,只会在一些特殊的场景下使用。虽然如此,我们学会它并不只是用来实现设计模式,更重要的是可以加深我们对于Python面向对象的理解。

以上内容为大家介绍了Python之__init__和__new__的区别是什么,希望对大家有所帮助,如果想要了解更多Python相关知识,请关注IT培训机构:千锋教育。

声明:本站部分稿件版权来源于网络,如有侵犯版权,请及时联系我们。

相关推荐

  • Python之列举常见的内置函数 abs()返回数字的绝对值map根据函数对指定序列做映射map()函数接收两个参数,一个是函数,一个是可迭代对象,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。返回值:Py
  • 列举Python面向对象中带双下划线的特殊方法 __new__:生成实例__init__:生成实例的属性__call__:实例对象加()会执行def__call__:...方法里边的内容。__del__:析构方法,当对象在内存中被释放时,自动触发执
  • Python之如何判断是函数还是方法? 看他的调用者是谁,如果是类,就需要传入一个参数self的值,这时他就是一个函数,如果调用者是对象,就不需要给self传入参数值,这时他就是一个方法print(isinstance(obj.func,F
  • Python静态方法和类方法区别? 尽管classmethod和staticmethod非常相似,但在用法上依然有一些明显的区别。classmethod必须有一个指向类对象的引用作为第一个参数,而staticmethod可以没有任何参数
  • Python3和Python2的区别? 1:打印时,py2需要可以不需要加括号,py3需要python2:print('lili'),print'lili'python3:print('lili&#3
  • Python切片操作的一般方式 一个完整的切片表达式包含两个“:”,用于分隔三个参数(start_index、end_index、step),当只有一个“:”时,默认第三个参数step=1。切片操作基本表达式:object[star