Алмазное наследование в Python с разными сигнатурами - PullRequest
0 голосов
/ 22 февраля 2019

Вот настройка:

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 с подписью Attacker __init__, конструктор Goalie вызывается с подписью Player, все идет нормально, поскольку save_probability имеет значение по умолчанию, но проблема в том, что у меня нет никакого способа выбрать save_probability, , кроме установки up.save_probability после создания экземпляра up, который я считаю очень нелегким.

Кроме того, Goalie не имелзначение по умолчанию для save_probability, этот код вызовет исключение.

Есть ли способ написать UniversalPlayer, чтобы я тоже мог выбрать save_probability, или есть какая-то фундаментальная проблема, которая не может быть решенавокруг?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Помимо того, что я не уверен, что отдельные классы являются лучшим способом их обработки, проблема в том, что ваши конструкторы не могут обрабатывать неизвестные аргументы.Чтобы позволить им использовать обозначение *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)
0 голосов
/ 22 февраля 2019

Каждый дополнительный параметр к __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__.

...