【问题标题】:Better way to access super class method in multiple inheritance在多重继承中访问超类方法的更好方法
【发布时间】:2011-09-14 12:00:50
【问题描述】:
class Animal(object):
    def eat(self):
        print("I eat all")

class C(object):
    def eat(self):
        print("I too eat")

class Wolf(C, Animal):
    def eat(self):
        print("I am Non Veg")
        super(Wolf, self).eat()
        Animal.eat(self)

w = Wolf()
w.eat()

我正在学习python中的多重继承,我想使用super方法从派生类访问AnimalC方法eat

默认调用super内部调用C类方法eat,但是调用Animal类方法我使用Animal.eat(self)。我的问题是如何调用Animal类方法使用super方法。

【问题讨论】:

标签: python


【解决方案1】:

如果你想做合理的多重继承,你需要每个人都调用super,除了一个基类,它有责任不调用super。使用相同的方法拥有两个完全独立的基类在使用 OOP 理论时是没有意义的,也不是 Python 有工具可以很好地处理的东西。

您可能会看到在很多示例中使用了两个明显独立的基类,但在这些情况下,示例方法通常是 __init__,而它的单个非超级调用基类是 object

【讨论】:

  • Having two completely-independent base classes with the same method isn't something that makes sense using OOP theory and is not something that Python has the tooling to handle well. mixins 试图修饰行为呢?
【解决方案2】:

您不能使用super 呼叫Animal.eatsuper 使用 Python's Method Resolution Order (MRO) 来确定要调用哪个类,而 C 在该 MRO 中使 Animal 黯然失色。

在您的示例中,super(Wolf, self).eat()C.eat(self) 具有相同的效果。调用 Animal.eat(self) 有效,但前提是继承图保持当前状态。

由于 MRO 不得不调用一个不可用的方法,这表明类建模需要改进。

【讨论】:

  • super(Wolf, self).eat() 只是Wolf.eat(self). 的语法糖 不,不是——它只是普通语法。这两个调用在语义上是不同的,因为第一个调用 MRO 并且可能为 Wolf 的不同实例做不同的事情(取决于更专业的类子类),而第二个调用只是调用 Wolf.eat,在这种情况下将导致到无限递归。 (在这段代码中,前者实际上恰好调用了C.eat。)调用Animal.eat(self) 非常好 除非您希望任意继承以允许只调用一次所有父方法。
  • @Mike Graham 是正确的。 super(Wolf, self.).eat() 不是 Wolf.eat(self) 的糖,这将是对子类的递归调用。 super(Wolf, self).eat() 调用父类的eat()方法。
  • @Mike Graham 是的,语法糖在这里是错误的术语。修复了 super 到 C.eat 的展开,并澄清了其余部分。谢谢!
  • @phihag,您的编辑做出了一些很好的改进,但我仍然认为您提供的建议有些不稳定。直接用superAnimal.eat 调用C.eat 不能确保它们都被调用一次,因为Wolf 的另一个子类的MRO 可能不是你所期望的。
  • @Mike Graham 你能举一个子类的例子吗自己的恶作剧?我的印象是,MRO 的标准之一就是 CWolf 的任何子类的任何线性化中总是出现在 Wolf 之前。
【解决方案3】:

super 用于在父类中查找。在您的情况下,因为您按C, Animal 的顺序设置继承,所以方法查找的优先顺序如下:

  • 第一个父类
  • 第二个父类

所以super 将返回最近的父方法。

不过,要注意的是,搜索是以深度优先的方式完成的:

>>> class A(object):
...      def t(self):
...          print 'from A'
>>> class B(object):
...      def t(self):
...          print 'from B'
>>> class C(A):
...      pass
>>> class D(C, B):
...      pass
>>> d = D()
>>> d.t()
from A

【讨论】:

  • class D(C, B)换成class D(B, C):后试一试,找出区别
  • 是的,结果不同,因为 Python 将首先在类 B 中搜索,然后找到返回 from B 的方法 t。在我的示例中,它在 C 中搜索,然后在其父项中搜索,之前B 中搜索。
  • 我没有看到 super 在这里被使用。在此示例中如何使用它来获得相同的结果?
【解决方案4】:

super() 使用Method Resolution Order (MRO) 来确定调用哪个超类方法。

在多重继承的情况下控制哪个类方法的最好方法是显式调用它,而不使用super()

或者您可以重新排序子类定义中的超类以更改 MRO。这允许您继续使用super(),但牺牲了一些可读性,IMO。

使用super()的示例:

class Wolf(C, Animal):
    def eat(self):
        # C.eat() comes first in the MRO
        super(Wolf, self).eat()

对比:

class Wolf(Animal, C):
    def eat(self):
        # Animal.eat() comes first in the MRO
        super(Wolf, self).eat()

【讨论】:

  • 这通常也不起作用,因为Wolf 的子类可能具有看起来与您预期不同的 MRO。
  • @Mike Graham:此示例演示了如何仅针对 Wolf 类控制 MRO,而不针对 Wolf 的可能子类,但您提出了一个很好的观点。
  • 对,但是super 的存在是为了解决在存在任意继承的情况下只调用每个超类方法一次的问题。如果我们不担心额外的继承,我们一开始就不需要super
  • super() 只调用一个超类方法,而不是每个方法。即使没有super(),这也可以通过显式调用超类方法来完成,但super() 很好,因为它在继承层次结构上提供了一层抽象。
  • super 只调用一个超类方法。当 everyone 使用 super 时,除了不存在的基类之外,它们都只被调用一次,这就是 super 的用途。 super 的存在是因为您无法通过显式调用基类方法获得相同的行为。
猜你喜欢
  • 2018-01-05
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多