python中子类调用父类的方法

本文探讨了在Python中如何使用super()调用父类方法,从Python 2.2引入super关键字的原因和使用方法出发,分析了在多继承场景下可能出现的问题以及解决方案。通过实例展示了super()如何避免重复调用初始化函数,并解释了mro(方法解析顺序)在处理多继承中的作用。最后总结了super的使用注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python super()

一、问题的发现与提出

  在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:

 代码段1:

class A:
  def __init__(self):
    print "enter A"
    print "leave A"

class B(A):
  def __init__(self):
    print "enter B"
    A.__init__(self)
    print "leave B"

 >>> b = B()
 enter B
 enter A
 leave A
 leave B

即,使用非绑定的类方法(用类名来引用的方法),并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。

  这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2,

 代码段2:

class B(C):    # A --> C
  def __init__(self):
    print "enter B"
    C.__init__(self) # A --> C
    print "leave B"

  如果代码简单,这样的改动或许还可以接受。但如果代码量庞大,这样的修改可能是灾难性的。

  因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:

 super(type[, object-or-type])

  Return the superclass of type. If the second argument is omitted the super object
  returned is unbound. If the second argument is an object, isinstance(obj, type) 
  must be true. If the second argument is a type, issubclass(type2, type) must be 
  true. super() only works for new-style classes.

  A typical use for calling a cooperative superclass method is:

   class C(B):
       def meth(self, arg):
           super(C, self).meth(arg)

  New in version 2.2.

从说明来看,可以把类B改写如代码段3:

 代码段3:

class A(object):    # A must be new-style class
  def __init__(self):
    print "enter A"
    print "leave A"

class B(C):     # A --> C
  def __init__(self):
    print "enter B"
    super(B, self).__init__()
    print "leave B"

        尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。

  在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。

  有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:

class A(object):
  def __init__(self):
    print "enter A"
    print "leave A"
class B(object):
  def __init__(self):
    print "enter B"
    print "leave B"
class C(A):
  def __init__(self):
    print "enter C"
    super(C, self).__init__()
    print "leave C"
class D(A):
  def __init__(self):
    print "enter D"
    super(D, self).__init__()
    print "leave D"
class E(B, C):
  def __init__(self):
    print "enter E"
    B.__init__(self)
    C.__init__(self)
    print "leave E"
class F(E, D):
  def __init__(self):
    print "enter F"
    E.__init__(self)
    D.__init__(self)
    print "leave F"

  明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:

    object
   |       \
   |        A
   |      / |
   B  C  D
    \   /   |
      E    |
        \   |
          F

  按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!

  也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:

 F E B C D A object

  从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。 ???

我们把代码段4改写为:

 代码段9:

class A(object):
  def __init__(self):
    print "enter A"
    super(A, self).__init__()  # new
    print "leave A"
class B(object):
  def __init__(self):
    print "enter B"
    super(B, self).__init__()  # new
    print "leave B"
class C(A):
    def __init__(self):
    print "enter C"
    super(C, self).__init__()
    print "leave C"
class D(A):
  def __init__(self):
    print "enter D"
    super(D, self).__init__()
    print "leave D"
class E(B, C):
  def __init__(self):
    print "enter E"
    super(E, self).__init__()  # change
    print "leave E"
class F(E, D):
  def __init__(self):
    print "enter F"
    super(F, self).__init__()  # change
    print "leave F"

f = F() result:

 enter F
 enter E
 enter B
 enter C
 enter D
 enter A
 leave A
 leave D
 leave C
 leave B
 leave E
 leave F

  明显地,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。

  再看类结构:

bject / \ / A | / \ B-1 C-2 D-2 \ / / E-1 / \ / F

E-1,D-2是F的父类,其中表示E类在前,即F(E,D)。

所以初始化顺序可以从类结构图来看出 : F->E->B -->C --> D --> A

由于C,D有同一个父类,因此会先初始化D再是A。

二、延续的讨论

  我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不能发现mro的顺序正好是该有向图的一个拓扑排序序列。

  从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。

  但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。

三、小结

  1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
       产生了一个super对象;
  2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
  3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
  4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
       只调用一次(如果每个类都使用super);
  5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
       个父类函数被调用多次。

转自:http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html

<think>好的,我现在需要解决用户的问题:如何在Python子类中正确调用父类方法,特别是使用super()的示例。首先,我应该回顾用户提供的引用内容,看看有没有相关的信息。根据引用[1]到[5],用户主要提到了两种方法:显式调用父类方法(如ParentClass.method(self))和使用super()函数。用户特别提到想了解super的示例,所以重点应该放在super上。 接下来,我需要确保自己正确理解super()的用法。在Python中,super()用于调用父类方法,尤其是在子类覆盖了父类方法的情况下。当使用super()时,尤其是在多重继承中,它会按照方法解析顺序(MRO)来查找父类方法。这里可能需要解释单继承和多重继承的情况。 然后,我需要构建一个清晰的示例。用户可能需要一个简单的例子来展示基本用法,再一个稍微复杂一点的例子来展示多重继承的情况。例如,在单继承中,子类的__init__方法中使用super().__init__()来调用父类的构造函数。在方法覆盖时,子类方法中使用super().method()来调用父类的同名方法。 还要注意Python 3和Python 2在super()使用上的区别。Python 3中可以直接用super()而不带参数,而Python 2需要传递和实例,但用户可能现在主要使用Python 3,所以示例应基于Python 3的语法。 另外,用户引用的内容中提到显式调用和使用super()的区别。需要指出显式调用的缺点,比如在多重继承时可能重复调用父类方法,而super()可以避免这种情况,遵循MRO。 可能还需要提到当子类重写父类方法时,如何扩展而不是完全覆盖父类方法。例如,在子类方法中先调用super().method(),然后再添加子类特有的代码。 最后,总结两种方法的适用场景,建议在大多数情况下使用super(),因为它更灵活和符合Python的最佳实践,尤其是在处理复杂的继承结构时。 现在需要检查是否有遗漏的点,比如错误处理,或者常见的误区,比如在多重继承中super()的行为。另外,要确保示例代码正确,语法无误,符合用户提到的LaTeX格式要求,比如行内公式用$...$,独立公式用$$...$$,但这个问题中没有数学公式,所以可能不需要。但需要确保代码块正确使用Python语法高亮,如用户提供的示例中的代码块。 可能还需要回答用户生成的相关问题,比如super()的工作原理,如何处理多重继承,或者何时使用显式调用。但根据用户的问题,主要需要提供示例和解释super()的正确用法。</think>在Python中,子类调用父类方法主要有两种方式:**显式调用**和**使用`super()`函数**。以下是具体实现方法及示例: --- ### 一、显式调用父类方法 通过`ParentClassName.method(self)`直接指定父类方法调用,适用于简单继承场景。 #### 示例代码 ```python class Parent: def show(self): print("父类方法调用") class Child(Parent): def show(self): Parent.show(self) # 显式调用父类方法 print("子类新增内容") child = Child() child.show() ``` **输出结果**: ``` 父类方法调用 子类新增内容 ``` --- ### 二、使用`super()`函数(推荐) `super()`根据方法解析顺序(MRO)自动查找父类,适用于单继承和多重继承,更灵活且符合Python规范。 #### 1. 单继承示例 ```python class Parent: def __init__(self): self.value = 100 def calculate(self): return self.value * 2 class Child(Parent): def __init__(self): super().__init__() # 调用父类初始化方法 self.extra = 50 def calculate(self): base = super().calculate() # 调用父类calculate方法 return base + self.extra child = Child() print(child.calculate()) # 输出:250 ``` #### 2. 多重继承示例 ```python class A: def process(self): print("A处理逻辑") class B(A): def process(self): super().process() print("B增强逻辑") class C(A): def process(self): super().process() print("C增强逻辑") class D(B, C): def process(self): super().process() print("D最终处理") d = D() d.process() ``` **输出结果**(按MRO顺序执行): ``` A处理逻辑 C增强逻辑 B增强逻辑 D最终处理 ``` --- ### 三、关键区别与选择建议 1. **显式调用** - 优点:简单直观 - 缺点:硬编码父类名,多重继承时可能导致重复调用[^2] 2. **`super()`函数** - 优点:动态解析父类,支持协作式多重继承[^3] - 注意:Python 3中`super()`可省略参数,Python 2需写为`super(Child, self).method()` --- ### 四、常见问题解答 #### 1. 为什么推荐使用`super()`? - 避免父类多次初始化(如菱形继承问题) - 代码更易维护,父类名修改不影响子类[^4] #### 2. `super()`在多重继承中的执行顺序? 由`名.__mro__`属性决定,可通过`print(D.__mro__)`查看具体顺序。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值