python - Python中的一对一继承

标签 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

如果我当时采用我最初的方法,并遵循我在使用父类(super class)构造函数方面的一贯做法,我显然最终会调用 Actor 构造函数两次 - 而不是问题,但我的程序员感到刺痛并说我宁愿用更干净的方式来做;

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

例如,我可以只调用Mover构造函数,并简单地初始化Human_range明确地,但这让我觉得这是一种更糟糕的方法,因为它复制了 Attacker 的初始化代码。

正如我所说,我知道将 _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__

发生的事情是 super(B, self) 调用 D 的实例知道 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!

请注意 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()

关于python - Python中的一对一继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11932013/

相关文章:

c++ - 错误继承无法访问方法 C++

c++ - 被多重继承隐藏的嵌套类

Python:如何修改字典列表中的特定(键,值)对?

python - 使用 Selenium 登录 Microsoft 帐户

c++ - 使用继承拆分模板代码是正确的方法吗?

function - 具有虚函数的类中的非虚函数

java - 我似乎无法让这个继承发挥作用

c++ - 多重继承运算符()的重载解决方案

Python 使读取 Excel 文件的速度更快

python - 基于MultiColumn二级的圆柱