编程语言
首页 > 编程语言> > Python中的一对多继承

Python中的一对多继承

作者:互联网

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

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

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

但是我反对这样一个事实,即Actor有一个我想要初始化的属性,来自Mover和Attacker子类中的每一个,如下面所示;

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构造函数两次 – 不是问题,但我的程序员感觉到了tingles并说我宁愿做得更干净;

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

例如,我只能调用Mover构造函数,只是简单地初始化了Human的_range,但这对我来说是一个更糟糕的方法,因为它复制了攻击者的初始化代码.

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

解决方法:

你在这里得到的是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__

发生的事情是,在D的实例上调用的super(B,self)知道C在方法解析顺序中是下一个,所以它转到C而不是直接转到A.我们可以通过查看MRO来检查:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

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

请注意,超级绝对不是魔术;它的作用可以用Python本身近似(这里仅用于super(cls,obj)功能,使用闭包来绕过__getattribute__ circularity):

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()

标签:diamond-problem,python,inheritance,multiple-inheritance
来源: https://codeday.me/bug/20190901/1786608.html