Python中的一对多继承

unw*_*ing 3 python inheritance multiple-inheritance diamond-problem

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

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

class Actor
  class Mover(Actor)
  class Attacker(Actor)
    class Human(Mover, Attacker)
Run Code Online (Sandbox Code Playgroud)

但我反对的事实跑起来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
Run Code Online (Sandbox Code Playgroud)

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

class Human(Mover, Attacker):
    def __init__(self, world, speed, range):
        Mover.__init__(self, world, speed)
        Attacker.__init__(self, world, range)
Run Code Online (Sandbox Code Playgroud)

只能调用Mover构造函数,例如,简单地初始化Human_range明确,但在我这个跳出一个糟糕的方法,因为它复制了初始化代码的Attacker.

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

eca*_*mur 5

你在这里得到的是钻石继承.Python对象模型通过方法解析顺序算法解决了这个问题,该算法使用C3线性化 ; 实际上,你所要做的就是使用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)
Run Code Online (Sandbox Code Playgroud)

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

human = Human(world=world, range=range, speed=speed)
Run Code Online (Sandbox Code Playgroud)

这里到底发生了什么?如果您对__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.我们可以通过查看MRO来查看:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
Run Code Online (Sandbox Code Playgroud)

为了更好地理解,请阅读Python的super()认为超级!

注意super绝对不是魔术; 它的作用可以用Python本身近似(这里只是为了super(cls, obj)功能,使用闭包来绕过__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()
Run Code Online (Sandbox Code Playgroud)