3

关于我是否会以最好的方式做某事的问题......

我想在 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' _range,但这是一种糟糕的方法,因为它复制了 . 的初始化代码Attacker

就像我说的,我知道将_world属性设置两次没什么大不了的,但是您可以想象,如果在 中进行更密集的操作Actor.__init__,这种情况会令人担忧。任何人都可以建议在 Python 中实现这种结构的更好做法吗?

4

1 回答 1

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)

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

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)的实例上调用,所以它转到而不是直接到。我们可以通过查看 MRO 来检查:DCCA

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

为了更好地理解,请阅读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()
于 2012-08-13T10:07:59.480 回答