Python中的一对多继承

时间:2012-08-13 10:00:02

标签: python inheritance multiple-inheritance diamond-problem

关于我是否以最佳方式处理某事的问题......

我想在Python中有一个类层次结构,它看起来(最低限度)如下所示;

class Actor
  class Mover(Actor)
  class Attacker(Actor)
    class Human(Mover, Attacker)

但是我反对这样一个事实:Actor具有某个属性,我想从每个MoverAttacker子类初始化,例如下面的内容;

class Actor:
    _world = None
    def __init__(self, world):
        self._world = world

class Mover(Actor):
    _speed = 0
    def __init__(self, world, speed):
        Actor.__init__(self, world)
        self._speed = speed

class Attacker(Actor):
    _range = 0
    def __init__(self, world, range):
        Actor.__init__(self, world)
        self._range = range

如果我按照我最初的方法去做,并遵循我在使用超类'构造函数方面的常规,我显然最终会调用Actor构造函数两次 - 而不是问题,但我的程序员感觉到了刺痛,并说我宁愿做得更干净;

class Human(Mover, Attacker):
    def __init__(self, world, speed, range):
        Mover.__init__(self, world, speed)
        Attacker.__init__(self, world, range)

例如,我 只能调用Mover构造函数,并且只是明确地初始化Human的{​​{1}},但这突然出现在我面前一个很多更差的方法,因为它复制了_range的初始化代码。

就像我说的那样,我知道将Attacker属性设置两次并不是什么大不了的事,但你可以想象如果在_world中发生了更为密集的事情,这种情况会让人担心。任何人都可以建议在Python中实现这个结构的更好的做法吗?

1 个答案:

答案 0 :(得分:5)

你在这里被称为diamond inheritance。 Python对象模型通过method resolution order算法解决了这个问题,该算法使用C3 linearization;实际上,您所要做的就是使用super并通过**kwargs(在Python 2中,继承自object):

class Actor(object):    # in Python 3, class Actor:
    _world = None
    def __init__(self, world):
        self._world = world

class Mover(Actor):
    _speed = 0
    def __init__(self, speed, **kwargs):
        super(Mover, self).__init__(**kwargs)    # in Python 3, super().__init__(**kwargs)
        self._speed = speed

class Attacker(Actor):
    _range = 0
    def __init__(self, range, **kwargs):
        super(Attacker, self).__init__(**kwargs) # in Python 3, super().__init__(**kwargs)
        self._range = range

class Human(Mover, Attacker):
    def __init__(self, **kwargs):
        super(Human, self).__init__(**kwargs)    # in Python 3, super().__init__(**kwargs)

请注意,您现在需要使用kwargs样式构建Human

human = Human(world=world, range=range, speed=speed)

这里究竟发生了什么?如果您检测到__init__次调用(将类重命名为A, B, C, D以获得简洁性):

  • D.__init__来电B.__init__
    • B.__init__来电C.__init__
      • C.__init__来电A.__init__
        • A.__init__来电object.__init__

正在发生的事件super(B, self)调用了D的实例,知道方法解析顺序中的C是下一个,所以它转到C而不是直接转到A >>> D.__mro__ (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) 。我们可以通过查看MRO来查看:

super

为了更好地理解,请阅读Python’s super() considered super!

请注意super(cls, obj)绝对不是魔法;它的作用可以用Python本身近似(这里仅用于__getattribute__功能,使用闭包来绕过def super(cls, obj): mro = type(obj).__mro__ parent = mro[mro.index(cls) + 1] class proxy(object): def __getattribute__(self, name): return getattr(parent, name).__get__(obj) return proxy() 循环度):

{{1}}