【问题标题】:How to use multiple (re-)inheritance properly如何正确使用多个(重新)继承
【发布时间】:2014-11-12 15:38:15
【问题描述】:

我面临以下问题:

  • 有一个基类Unit,它有几个属性,例如id, type, name, skills, ...
  • 有不同类型的单元,其中一些具有healthattacktribe等附加属性,因此自然也存在相关子类HealthUnitAttackUnit等。
  • 有一些单位具有多个这些属性,例如HealthAttackUnit,或HealthAttackTribeUnit

我想避免这样的编码:

class Unit(object):
    def __init__(self, id, type, name, skills):
        self.id= id
        self.type= type
        self.name= name
        self.skills= skills

class HealthUnit(Unit):
    def __init__(self, id, type, name, skills, health):
        Unit.__init__(self, id, type, name, skills)
        self.health= health

class AttackUnit(Unit):
    def __init__(self, id, type, name, skills, attack):
        Unit.__init__(self, id, type, name, skills)
        self.attack= attack

class HealthAttackUnit(HealthUnit, AttackUnit):
    def __init__(self, id, type, name, skills, health, attack):
        HealthUnit.__init__(self, id, type, name, skills, health)
        AttackUnit.__init__(self, id, type, name, skills, attack)

出于显而易见的原因。


我尝试使用 dict 解包作为解决方法,有点像这样:

class HealthUnit(Unit):
    def __init__(self, health, **args):
        Unit.__init__(self, **args)
        self.health= health

但即使这样也有很多重复的代码:

class HealthAttackUnit(HealthUnit, AttackUnit):
    def __init__(self, health, attack, **args):
        HealhUnit.__init__(self, health=health, **args)
        AttackUnit.__init__(self, attack=attack, **args)

class HealthAttackTribeUnit(HealthUnit, AttackUnit, TribeUnit):
    def __init__(self, health, attack, tribe, **args):
        HealhUnit.__init__(self, health=health, **args)
        AttackUnit.__init__(self, attack=attack, **args)
        TribeUnit.__init__(self, tribe=tribe, **args)

另外,这将调用Unit.__init__ 多次,这不太理想。

所以,问题是:有没有更好、更少复制/粘贴的方法?

更新: dict 解包非常好,但使用关键字参数调用所有构造函数仍然有点烦人。我更喜欢没有**kwargs 的解决方案,但我猜可能没有?

【问题讨论】:

  • 如果您使用super(),则不必分别调用每个继承的__init__ - 参见例如stackoverflow.com/q/576169/3001761。你也需要使用*args, **kwargs
  • 除非您实例化HealthUnitAttackUnit 的对象,否则您可能希望对mixins 感兴趣。
  • @poke 那些 mixins,确定吗?
  • @DanielRoseman 因为他们继承自一般的Unit,而不是只是提供健康或攻击的东西,我不这么认为。
  • @DanielRoseman:不,它们不是 mixins,因为需要实例化 HealthUnitAttackUnit 对象。

标签: python python-2.7 inheritance multiple-inheritance


【解决方案1】:

是的,这正是super 函数存在的原因。

确保你所有的__init__文件都调用super,Python会为你制定MRO并依次调用相关的类。

class HealthUnit(Unit):
    def __init__(self, **kwargs):
        self.health = kwargs.pop('health')
        super(HealthUnit, self).__init__(**kwargs)

class AttackUnit(Unit):
    def __init__(self, **kwargs):
        self.attack = kwargs.pop('attack')
        super(AttackUnit, self).__init__(**kwargs)

class TribeUnit(Unit):
    def __init__(self, **kwargs):
        self.tribe = kwargs.pop('tribe')
        super(TribeUnit, self).__init__(**kwargs) 

class HealthAttackTribeUnit(HealthUnit, AttackUnit, TribeUnit):
    pass

另请参阅 Python 核心贡献者(以及偶尔的 SO 海报)Raymond Hettinger 的文章 Super considered super,但请注意该文章中的语法适用于 Python 3,版本 2 代码有一个单独的链接。

【讨论】:

  • 我不确定我真的会 remove 例如kwargs['health'] 在调用超类之前,我会做例如kwargs.get('health'),它还允许设置默认值。否则 +1。
  • 我这样做的原因是更高层次的类可能不会期待这些参数:如果它们没有设置为接受**kwargs,它将导致 TypeError。如果您一直到object,肯定会发生这种情况。我认为最好在处理它们时删除这些参数。
  • 这是一个很好的解决方案,但它迫使我使用关键字参数调用所有构造函数。这不是一个真正的问题,但我仍然想问:有没有办法绕过 dict 解包? P.S.:我终于明白super做了什么,谢谢,并接受我的投票,好心的先生。
  • 使用 args 而不是 kwargs 并没有真正的好方法。问题是您需要保证 MRO,这取决于继承图。如果您有兴趣,Raymond 的文章会详细介绍具体是如何计算的,但即使他也建议使用 kwargs 而不是 args 来完成类似的事情。
  • 我明白了。那好吧。我只是将它添加到我不喜欢 python 的一堆东西中。
猜你喜欢
  • 2014-07-13
  • 2019-12-24
  • 2010-11-17
  • 2011-05-03
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-28
相关资源
最近更新 更多