super真的是调用父类吗?

Python的super函数常被误解为调用父类,实际上它遵循MRO(Method Resolution Order)调用下一个类的方法。在新式类中使用super避免硬编码,但不适用于多重继承。super()在Python3后简化,不再需要显式传参。理解super与MRO的关系,以及在构造函数中的作用,有助于避免代码问题。

可能有人会想到,Python中既然可以直接通过父类名调用父类方法为什么还会存在super函数?其实,很多人对Python中的super函数的认识存在误区
在子类需要调用父类的方法时,在python2.2之前,直接用类名调用类的方法,即非绑定的类方法,并把自身对象self作参数传进去。

class A(object): 
  def say(self): 
    print 'I am A'

class B(A): 
  def say(self): 
    print 'I am B'
    A.say(self) 

b = B() 
b.say() 

输出  
I am B
I am A

这样运作挺好,不过有个问题,当父类改了名字时,就要把这些显式调用父类的一个个更正,子类和父类耦合比较高。
于是python2.2后就推出了super()函数来避免硬编码,不用关心父类名叫什么。
使用super()函数,上面的代码可以写成如下。

class B(A): 
  def say(self): 
    print 'I am B'
    super(B,self).say() 

python3.0后,又做了改良,super()函数不用传参数,即上面的那行代码直接super().say()就行了。

需要注意的问题:
super只能用在新式类中。
super在多重继承有问题,如果子类继承多个父类,那么super调用第一个父类的方法。
不要混用这两种调用父类方法的方案,要么都用非绑定的类方法,要么都用super。不然可能导致没被调用或者被调用多次。

BUT:
不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!



再次提出两个疑问:我们已经重写了构造函数,为什么还要调用父类?
super的执行顺序是什么样的?

对于第一个问题比较好理解,调用父类是避免代码重复,可以直接重用父类代码

对于第二个问题
看个简单例子
这里写图片描述
这个例子中B继承了A,super直接调用了父类的__init__方法

再看个例子:
一说到 super 就想到父类这是初学者很容易犯的一个错误

def super(cls, inst):
  mro = inst.__class__.mro()
  return mro[mro.index(cls) + 1]

两个参数 cls 和 inst 分别做了两件事:
1. inst 负责生成 MRO 的 list
2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]
这两件事才是 super 的实质,一定要记住!

MRO 全称 Method Resolution Order,它代表了类继承的顺序。

class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        print('B')
        super().__init__()

class C(A):
    def __init__(self):
        print('C')
        super().__init__()

class D(B, C):
    def __init__(self):
        print('D')
        super().__init__()

if __name__ == '__main__':
    d = D()
    print(D.__mro__)

#结果
D
B
C
A
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

所以调用的不是父类,而是mro顺序的的下一个类

再举个例子:

class Root(object):
  def __init__(self):
    print("this is Root")

class B(Root):
  def __init__(self):
    print("enter B")
    # print(self) # this will print <__main__.D object at 0x...>
    super(B, self).__init__()
    print("leave B")

class C(Root):
  def __init__(self):
    print("enter C")
    super(C, self).__init__()
    print("leave C")

class D(B, C):
  pass

d = D()
print(d.__class__.__mro__)

输出

enter B
enter C
this is Root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__m

知道了 super 和父类其实没有实质关联之后,我们就不难理解为什么 enter B 下一句是 enter C 而不是 this is Root(如果认为 super 代表“调用父类的方法”,会想当然的认为下一句应该是this is Root)。流程如下,在 B 的 init 函数中:

super(B, self).__init__()

首先,我们获取 self.class.mro,注意这里的 self 是 D 的 instance 而不是 B 的

然后,通过 B 来定位 MRO 中的 index,并找到下一个。显然 B 的下一个是 C。于是,我们调用 C 的 init,打出 enter C。

为什么 B 的 init 会被调用:因为 D 没有定义 init,所以会在 MRO 中找下一个类,去查看它有没有定义 init,也就是去调用 B 的 init

<think>好的,用户想了解Java中super关键字调用父类方法的用法和原理。首先,我需要回顾一下super关键字的基本概念,确保自己理解正确。根据引用内容,super用于访问父类的成员,包括方法、属性和构造器。用户特别关注调用父类方法,所以需要重点解释这部分。 接下来,我应该整理super调用父类方法的具体用法。根据引用中的例子,比如在子类中重写父类方法时,用super.shout()可以调用父类被覆盖的方法。需要说明语法结构,以及何时需要使用super,比如当子类方法需要扩展父类功能而不是完全替换时。 然后,考虑原理部分。根据引用3,当子类实例调用方法时,this关键字指向的是子类实例,但通过super,可以明确指示使用父类的方法。这可能涉及到Java的方法分派机制,需要解释方法重写后的动态绑定,以及super如何绕过这种绑定直接访问父类方法。 还要注意使用场景,比如在构造器中super调用父类构造方法,但用户的问题是关于方法的,所以构造器的部分可能不需要深入,但可以简要提及。引用4和5中的例子展示了在构造器和方法中使用super的不同情况,但这里应聚焦方法调用。 用户可能存在的疑问包括:super是否只能在实例方法中使用?是否可以在静态方法中使用?需要指出super不能用于静态上下文,因为静态方法属于类而非实例。此外,是否必须要有继承关系,super只能用于子类中。 另外,需要强调super和this的区别,比如this指向当前对象,而super指向父类部分。但两者都是在实例方法中使用,且不能用于静态方法。 可能的误区是认为super调用父类方法会创建父类实例,但实际上子类实例包含父类的部分,super只是访问这部分。引用3中的例子显示,父类方法中的this仍然是子类实例,说明super只是指定了方法查找的起始点,而不是切换对象。 最后,总结super的用法步骤,并给出示例代码,帮助用户理解。还要考虑用户可能想进一步了解相关知识点,比如方法重写、多态性,或者super在构造器中的使用,所以生成相关问题时要覆盖这些方面。 检查是否有遗漏点,比如访问父类属性是否与调用方法类似,但用户的问题集中在方法调用,所以可以简要提及。确保所有信息准确,引用正确来源,比如引用2和5直接提到super调用父类方法和成员变量,引用3和5提供了代码示例,这些都需要在回答中正确引用。 现在组织回答结构,先用法后原理,分点说明,代码示例,注意事项,最后相关问题。确保语言简洁,符合中文表达,使用正确的LaTeX格式,但用户问题中没有数学公式,所以可能不需要使用,但按系统指令,行内变量如方法名可能需要用$...$,但Java方法名通常不需要,可能只需代码块。不过根据示例中的Python代码,这里应该用代码块展示Java示例。</think>### super关键字调用父类方法的用法与原理 #### 一、基本用法 `super`关键字在子类中用于**显式调用父类的方法**,尤其是在以下两种场景中: 1. **子类重写父类方法时** 当子类需要扩展父类方法功能而非完全覆盖时,可用`super.方法名()`调用父类原始方法。 **示例**(引用自[^5]): ```java class Dog extends Animal { @Override void shout() { super.shout(); // 调用父类被重写的方法 System.out.println("汪汪!"); } } ``` 此时子类`shout()`方法会先执行父类的`shout()`,再添加子类特有逻辑。 2. **访问父类成员方法** 若父类方法未被重写,可直接通过`super.方法名()`调用,但通常省略`super`(隐式调用)[^2]。 --- #### 二、核心原理 1. **动态绑定与作用域限定** Java的方法调用默认基于**动态绑定**(运行时根据对象类型决定调用哪个方法)。通过`super`关键字,开发者可以**显式指定从父类作用域开始查找方法**,绕过子类重写的方法[^3]。 2. **内存结构** 子类实例包含**父类成员副本**。使用`super`时,不会创建新的父类对象,而是直接操作子类实例中继承父类部分[^4]。 --- #### 三、注意事项 1. **静态方法不可用** `super`不能用于调用父类的静态方法,静态方法通过类名直接调用[^2]。 2. **必须位于子类实例方法中** `super`只能在实例方法或构造器中使用,不可在静态上下文中使用。 3. **层级限制** `super`仅能访问直接父类的方法,若需调用更高层父类的方法,需通过继承链逐层访问。 --- #### 四、代码示例解析 ```java // 父类定义(引用自[^3]) class Parent { public void print() { System.out.println("Parent's print: this=" + this); } } // 子类通过super调用父类方法 class Child extends Parent { @Override public void print() { super.print(); // 调用父类print() System.out.println("Child's print"); } } ``` 运行结果: ``` Parent's print: this=Child@1b6d3586 Child's print ``` **关键点**:父类方法中的`this`仍指向子类实例,说明`super`仅改变方法查找路径,未改变对象本身[^3]。 --- #### 五、常见误区 - **误区**:`super`会创建父类对象。 **正解**:`super`仅指定方法调用的作用域,实际操作的仍是子类实例。 - **误区**:`super`可跨越多层父类调用。 **正解**:`super`仅能访问直接父类,若需调用父类方法,需在父类中提供中间调用。 --- 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值