关于我是否以最佳方式处理某事的问题......
我想在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
构造函数两次 - 而不是问题,但我的程序员感觉到了刺痛,并说我宁愿做得更干净;
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中实现这个结构的更好的做法吗?
答案 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}}