Python super() 调用父类方法, 但是父类中 self 并不是父类的实例而是子类的实例?

小夜勃
  • 221

本人在学习Python的过程中发现了这么一个问题, 自己查阅了一点资料但是还是没有理解, 如下:

版本为python3.6

代码如下:

class X:
    def __init__(self):
        self.x = 10

    def f(self):
        return self.x


class Y(X):
    def __init__(self):
        self.x = 100

    def b(self):
        return super().f()

x = X()
y = Y()
print(y.b()) # -> 100, 为什么不是 10?

问题描述:
本人对于super()其实一直不是特别理解, 网上查了一下资料以后理解为super()会返回MRO列表里面的下一个类, 那么我把YMRO打印出来:

print('mro', Y.mro()) # mro [<class '__main__.Y'>, <class '__main__.X'>, <class 'object'>]

那么根据MRO列表, 下面的类就是X了, 那么super().f()实际就为X.f(), 那么里面的return self.x应该就是X实例, 可是实际运行结果显示self是子类Y的实例. 既然这里的super().f()调用了父类的方法, 里面的self不应该是指代父类实例么? 这是我难以理解的地方.

表述有点乱, 望有前辈能给予解答, 不胜感激!

问题补充:

写这个问题的时候又想到了一点:

继承的时候常常会有super().__init__()来调用父类的__init__()函数, 调用这个函数的时候里面的self其实也就指代了子类实例? 好像与之前的问题一个意思, 但是不知道怎么理解, 望能有前辈指点一下.

回复
阅读 3k
3 个回答

说一下self究竟是什么.

写这个的时候也没查阅文档,有些说法可能不是很准确.

类方法的第一个参数self是实例本身,也就是A=X()这个A.

In [1]: class A(object):
   ...:     def get_self(self):
   ...:         return self
   ...:

In [2]: instance = A()

In [3]: instance.get_self() == instance
Out[3]: True

这个还是很清楚的,我们确定了这个之后,再转头去看你举的例子.
我们都知道,子类的方法会覆盖父类的同名方法.
先看下边的例子:

class A(object):

    def a(self):
        pass
    def b(self):
        pass

class B(A):

    def b(self):
        pass

    def __init__(self):
        print super(B,self).a == self.a //True
        print super(B,self).b == self.b //False


def main():
    B()


if __name__ == '__main__':
    main()

我们看到,在子类没有覆盖父类的方法时,子类的方法是等于父类的方法的,很纯粹的等于,内存中地址都是一样的.
所以你那个例子中super().f()就和直接f()没区别的.
同样,在子类的实例里也不会莫名其妙冒出来一个父类的实例作为self参数传给f()方法,而且,你子类的init方法覆盖了父类的init方法,所以父类的init方法根本就没有执行.从那个角度来说,都是万万不会等于父类初始化的值的.

  1. 执行 y=Y() 这句只能生成一个实例,就是子类的实例
  2. 由于子类 Y 有自己的初始化方法,并且没有显示调用父类的初始化方法,所以 X 的初始化方法就不会被调用。
  3. YX 的子类, Y 就继承了 Xf() 方法,调用 super().f() 这句不会执行 X 的初始化函数。 super().f() 是一种简写形式,完整写法应该是 super(Y,self).f(),执行过程如下

    • 找到 Y 的父类(就是类 X)
    • 把类 Y 的对象 y 转换为其父类 X 的对象,并调用 f() 方法
    • 表面上调用主体是类X 的对象,其实就是对象 y
  4. xy 是两个不同的对象,它俩没有一毛钱关系

上面的代码加了些 log,再执行下看看; 有问题再交流~

class X:
    def __init__(self):
      print('x init')
      print(self)
      self.x = 10

    def f(self):
        return self.x


class Y(X):
    def __init__(self):
      print('y init')
      print(self)
      self.x = 100

    def b(self):
        return super().f()

x = X()
y = Y()
print(y.b()) 

super().f()在这里是super(Y, self).f(),self是Y的实例, 即方法调用的是父类的f()方法也就是f(self),但是传入参数self是Y的实例,那么self.x即100

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏