Правильный способ передачи собственной переменной в качестве аргумента родительскому методу mixin - PullRequest
1 голос
/ 07 февраля 2020

Мне нужно смоделировать воина и различные виды атак, которые он может выполнить. Идея состоит в том, чтобы использовать миксины для хранения логики атаки c. Мои классы определены следующим образом:

class Warrior:
    def __init__(self, energy):
        self.energy = energy


class TemplarKnight(Warrior, HandToHandCombatMixin):
    pass


class CombatMixin:
    def __init__(self):
        self.attacks_cost = {}

    def attack(self, attacker, attack_cost):
        if attacker.energy < attack_cost:
            print('Not enough energy to attack')
        else:
            attacker.energy -= attack_cost
            print('Attack!')


class HandToHandCombatMixin(CombatMixin):
    def __init__(self):
        super().__init__()
        self.attacks_cost['sword_spin'] = 10

    def sword_spin(self, attacker):
        return self.attack(attacker, self.attacks_cost['sword_spin'])

Но проблема возникает, когда я пытаюсь проверить эту настройку. Когда я делаю

class TestTemplarKnight(unittest.TestCase):
    def setUp(self):
        self.templar = TemplarKnight(energy=100)

    def test_templar_knight_can_sword_spin(self):
        self.templar.sword_spin(self.warrior)
        self.assertEquals(self.templar.energy, 90)

, я получаю

    def sword_spin(self, attacker):
        return self.attack(
>           attacker, self.attacks_cost['sword_spin'])
E       AttributeError: 'TemplarKnight' object has no attribute 'attacks_cost'

Кажется, что Python считает, что параметр self.attacks_cost (при вызове self.attack() внутри sword_spin() метода HandToHandCombatMixin класс) принадлежит классу TemplarKnight вместо HandToHandCombatMixin.

Как мне написать этот код, чтобы Python искал self.attacks_cost внутри HandToHandCombatMixin?

1 Ответ

3 голосов
/ 07 февраля 2020

Чтобы правильно использовать super, все участвующие в нем классы должны использовать его. Прямо сейчас, Warrior.__init__ вызывается первым, но он не использует super, поэтому HandToHandCombatMixin.__init__ никогда не вызывается.

Сделайте следующие дополнения:

class Warrior:
    def __init__(self, energy, <b>**kwargs</b>):
        <b>super().__init__(**kwargs)</b>
        self.energy = energy


class TemplarKnight(Warrior, HandToHandCombatMixin):
    pass


class CombatMixin:
    def __init__(self, <b>**kwargs</b>):
        <b>super().__init__(**kwargs)</b>
        self.attacks_cost = {}

    def attack(self, attacker, attack_cost):
        if attacker.energy < attack_cost:
            print('Not enough energy to attack')
        else:
            attacker.energy -= attack_cost
            print('Attack!')


class HandToHandCombatMixin(CombatMixin):
    def __init__(self, <b>**kwargs</b>):
        super().__init__(<b>**kwargs</b>)
        self.attacks_cost['sword_spin'] = 10

    def sword_spin(self, attacker):
        return self.attack(attacker, self.attacks_cost['sword_spin'])

Теперь, когда Вы создаете экземпляр TemplarKnight, вы гарантируете, что все методы __init__ были вызваны и в правильном порядке. В конце концов, один из вызовов super() вызовет object.__init__, после чего цепочка окончательно завершится. Если вы правильно обрабатываете аргументы ключевого слова, **kwargs будет пустым к тому времени, когда это произойдет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...