手记

python __new__()和__init__()的关系

我们不妨来看看init()和new()之间的关系
在python中创建类的一个实例时,如果该类具有new方法,会先调用new方法,new方法接受当前正在实例化的类作为第一个参数(这个参数的类型是type,这个类型在c和python的交互编程中具有重要的角色,感兴趣可以查阅资料),其返回值是本次创建产生的实例,也就是我们熟知的init方法中的第一个参数self。
那么就有一个问题,这个实例,我们是怎么得到的?
注意到有new方法的都是object类的后代,因此如果我们自己想要改写new方法(注意不改写时在创建实例的时候使用的是父类的 new方法,如果父类没有则继续上溯)可以通过调用object的new方法类得到这个实例(这实际上也和python中的默认机制基本一致)
举个例子:如下图

运行结果:

从上面的例子,我们可以看出几个问题:
  1. 如果有new方法,new方法会先于init方法被调用。
  2. new方法的第一个参数类型为type。
  3. 我们的确是使用object的new方法创建的实例。

那么,我们可不可以用其他类的new方法得到这个实例,只要那个类或者那个类的
祖先有这么一个new方法
例子如下图所示:

所以,我们是可以用其他类的__new__得到这个实例,于是,我们有一个大胆的结论:

_newinit就像这么一个关系,init提供生产的原料self(但并不保证这个原料来源正宗,像上面那样它用的是另一个不相关的类的new方法类得到这个实例),而init就用new__给的原料来完善这个对象(尽管它不知道这些原料是不是正宗的)

下面我们看一个在 python3.6中__new__和__init__的使用(还是上面的例子,这次,我们在__new__中实例化不可变化对象)

在上图中,我们有几个关键点,
特别重要,一定要记得return一个实例
  1. 我们用new方法去创建了几个属性。
  2. super关键的使用
    关于super,我们作简单介绍
    a. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,产生了一个super对象;
    b. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
    c. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
    d. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数,只调用一次(如果每个类都使用super);
    e. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次
    其实,我们可以简单理解成一个parent,调用父类就好了。在上图那条super语句好
    比就是,

  3. 我在网上看到了很多这种写法,但是当我在python3.6中使用时是会报错的

    我们后面多了几个参数,运行时会出现这种错误

    就是说,我们的父类中,其实是没有参数的,而我们这里调用父类的new时候,多加了几个参数,我想应该就是这种问题,是不是以前是可以这样做的,现在不允许了。

    最后我们结合使用,

运行结果:

说了这么多,其实还是回归到我们在Python面向对象的编程中对new的一句话:
_new()方法(静态方法)更像一个构造器(因为new()必须返回一个合法的实
例。(必须return,强调)在解释器调用
init()的时候,就把实例作为self传给它)。Python用户可以对内建类型进行派生,因此可以用new()方法来实例化不可变化对象(上面的例子,我们创建了ph,nm属性)。new()方法传入参数在类实例化操作时生成。new()调用父类的new__()来创建对象。

在我们的例子中已经体现的很明显了。

0人推荐
随时随地看视频
慕课网APP