159

这是我计划用于我的游戏的代码,但它抱怨 MRO 错误:

class Player:
    pass

class Enemy(Player):
    pass

class GameObject(Player, Enemy):
    pass

g = GameObject()
4

3 回答 3

191

GameObject的继承自Player and Enemy。因为Enemy 已经从 Python 继承,Player现在无法确定首先查找方法的类;要么Player,要么 on Enemy,这将覆盖Player.

你不需要在Enemy这里命名所有的基类;只是从那个类继承:

class GameObject(Enemy):
    pass

Enemy已经包含Player,您不需要再次包含它。

于 2015-03-23T16:06:00.820 回答
112

我将解释原始代码不起作用的原因。

在查找实例属性/方法时,Python 需要决定以何种顺序搜索(直接和间接)基类。它通过线性化继承图来做到这一点,即将基类图转换为序列,使用称为C3 或 MRO的算法。MRO 算法是一种独特的算法,它实现了几个理想的特性:

  1. 每个祖先类只出现一次
  2. 一个类总是出现在它的祖先之前(“单调性”)
  3. 同一个类的直接父类的出现顺序应与它们在类定义中列出的顺序相同(“一致的本地优先顺序”)
  4. 如果类的孩子A总是出现在类的孩子之前B,那么A应该出现在之前B(“一致的扩展优先顺序”)

使用您的代码,第二个约束要求Enemy首先出现;第三个约束要求Player首先出现。由于无法满足所有约束,python 报告您的继承层次结构是非法的。

如果您GameObject像这样切换基类的顺序,您的代码将起作用:

class GameObject(Enemy, Player):
    pass

这不仅仅是一个技术细节。在某些情况下(希望很少见),您可能需要考虑如果该方法在多个类中定义,则应该使用哪个类来获取您调用的方法。您定义基类的顺序会影响此选择。

于 2017-03-02T22:57:32.043 回答
12

你写的是你希望 aGameObject既是 aPlayer又是a Enemy。但是 anEnemy已经是Player. MRO 问题只是指出,如果您在 中有一个字段,则a在实例中Player要求该字段GameObject将是模棱两可的:它应该是您继承a的第一个字段还是您通过继承继承的那个字段?PlayerPlayerEnemy

但是你确定你不想在这里使用组合而不是继承吗?

class GameObject(object):
    def __init__(self):
        self.player = Player()
        self.enemy = Enemy()
于 2015-03-23T16:10:42.193 回答