Python中具有不同签名的钻石继承

时间:2019-02-22 16:36:34

标签: python multiple-inheritance diamond-problem

这是设置:

class Player(object):
    def __init__(self, heigth):
        self.heigth = heigth
        print('do not forget that this should happen once!')

class Attacker(Player):
    def __init__(self, heigth, goal_probability):
        super().__init__(heigth)
        self.goal_prob = goal_probability

    def hit(self):
        pass
        # implementation

class Goalie(Player):
    def __init__(self, heigth, save_probability=0.1):
        super().__init__(heigth)
        self.save_prob = save_probability

    def catch(self):
        pass
        # implementation

class UniversalPlayer(Attacker, Goalie):
    pass

up = UniversalPlayer(heigth=1.96, goal_probability=0.6)

所有操作均按预期进行:MRO首先选择Attacker,然后选择Goalie。我用UniversalPlayer的{​​{1}}签名来调用Attacker的构造函数,用__init__的签名来调用Goalie的构造函数,因为{ {1}}具有默认值,但问题是我除了实例化Player后设置save_probability之外,没有选择save_probability 的方法,我觉得很优雅。

此外,如果up.save_probability没有up的默认值,则此代码将引发异常。

是否有写Goalie的方法,以便我也可以选择save_probability,还是这里存在一些无法解决的基本问题?

2 个答案:

答案 0 :(得分:2)

__init__的每个附加参数都需要一个负责将其从对super的调用中删除的类,这样,在最终调用object.__init__时,您不会意外地传递任何参数对此。此外,每个方法都必须接受任意参数并将其传递给下一个可能处理的方法。

# Player will be responsible for height
class Player(object):
    def __init__(self, height, **kwargs):
        super().__init__(**kwargs)  # Player needs to use super too!
        self.height = height
        print('do not forget that this should happen once!')


# Attacker will be responsible for goal_probability
class Attacker(Player):
    def __init__(self, height, goal_probability, **kwargs):
        super().__init__(height, **kwargs)
        self.goal_prob = goal_probability

    def hit(self):
        pass


# Goalie will be responsible for save_probability
class Goalie(Player):
    def __init__(self, height, save_probability=0.1, **kwargs):
        super().__init__(height, **kwargs)
        self.save_prob = save_probability

    def catch(self):
        pass
        # implementation

class UniversalPlayer(Attacker, Goalie):
    pass

# Pass all arguments
# Life is easier if you stick to keyword arguments when using super().__init__
up = UniversalPlayer(height=1.96, goal_probability=0.6, save_probability=0.2)

现在,Attacker.__init__是第一个被调用的。它使用goal_probability,然后不将其传递给其他呼叫。它通过save_probability接受**kwargs并将其传递给Goalie.__init__以便最终接收。请注意,Attacker.__init__Goalie.__init__都不必在其参数列表中明确包含height;也可以通过**kwargs接受,最终由Player.__init__接收。

答案 1 :(得分:0)

除了我不确定单独的类是否是处理这些问题的最佳方法外,问题还在于您的构造函数无法处理未知的参数。允许他们使用*args, **kwargs表示法。 实际上,所有参数都将传递给每个__init__,未使用的参数将被忽略。

class Player(object):
    def __init__(self, *args, **kwargs):
        self.height = kwargs['height']

class Attacker(Player):
    def __init__(self, goal_probability, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.goal_prob = goal_probability

    def hit(self):
        pass
        # implementation

class Goalie(Player):
    def __init__(self, save_probability, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.save_prob = save_probability

    def catch(self):
        pass
        # implementation

class UniversalPlayer(Attacker, Goalie):
    pass

up = UniversalPlayer(height=1.96, goal_probability=0.6, save_probability=0.2)