Множественное наследование с super () в Python - PullRequest
1 голос
/ 07 августа 2020

Практикую множественное наследование в Python. Без класса Boss все идет хорошо. Любая помощь высоко ценится. Я упоминал: Как Python super () работает с множественным наследованием?

Обратная связь:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in 
 41 print(archer1.__str__())
 42 print('')
---> 43 boss = Boss("Boss", 50, 50, 100)
 44 print(boss.__str__())

 in __init__(self, name, power, agility, HP)
 27 class Boss(Worrior,Archer):
 28      def __init__(self, name, power, agility, HP):
---> 29      Worrior.__init__(self, name, power, HP)
 30          Archer.__init__(self, name, agility, HP)
 31      def __str__(self):

 in __init__(self, name, power, HP)
  7 class Worrior(Player):
  8     def __init__(self, name, power, HP):
----> 9     super().__init__(HP)
 10         self.name = name
 11         self.power = power

TypeError: __init__() missing 2 required positional arguments: 'agility' and 'HP'

Кажется, после того, как Worrior класс, а затем остановитесь.

class Player:
def __init__(self,HP):
    self.HP = HP 
def sign_in(self):
    print('player sign in')
# put the class want to extend from

class Worrior(Player):
def __init__(self, name, power, HP):
    super().__init__(HP)
    self.name = name
    self.power = power
# it's the toString() method in java
# need to override the dunder(magic) method
def __str__(self):
    return "The worrior's name: " f'{self.name} \n' \
        "He has the power:" f'{self.power}'

class Archer(Player):
def __init__(self, name, agility, HP):
    super().__init__(HP)
    self.name = name
    self.agility = agility
def __str__(self):
    return "The archer's name: " f'{self.name} \n' \
        "He has the agility:" f'{self.agility}'

class Boss(Worrior,Archer):
def __init__(self, name, power, agility, HP):
     Worrior.__init__(self, name, power, HP)
     Archer.__init__(self, name, agility, HP)
def __str__(self):
    return "The boss's name: " f'{self.name} \n' \
        "With Worrior's power " f'{self.power} \n' \
        "and With Archer's agilit" f'{self.agility}'\
        "The boss' HP is: " f'{self.HP}'
boss = Boss("Boss", 50, 50, 100)
print(boss.__str__())

Ответы [ 3 ]

1 голос
/ 07 августа 2020

Ссылка от @Thierry Lathuille является правильной для чтения, но я постараюсь добавить некоторые дополнительные пояснения. MRO для инициализатора - [Босс, Воин, Лучник, Игрок]. Это означает (несколько сбивает с толку), что когда Worrior вызывает super (), это на самом деле относится к Archer, а не Player. Если вы помещаете print(super().__init__) перед каждым вызовом метода, вы увидите такой вывод до вашего cra sh:

<bound method Worrior.__init__ of <__main__.Boss object at 0x10af5f780>>
<bound method Archer.__init__ of <__main__.Boss object at 0x10af5f780>>
Traceback (most recent call last):
...

Это основная ошибка ИМХО с множественным наследованием в Python , и я обычно не рекомендую это делать, если у вас нет инициализаторов с нулевым аргументом. раз.

Чтобы иметь возможность передавать ваши аргументы, вам нужно использовать ** kwargs. Кроме того, по мере их прохождения каждое уникальное имя аргумента будет удалено из kwargs. Это означает, что вы не можете повторно использовать name в качестве аргумента инициализации. Вместо этого я буду использовать worrior_name и archer_name. Итак, собрав все это вместе, следующее будет запускать каждый инициализатор только один раз без сбоев:

class Player:
    def __init__(self, hp, **kwargs):
        print(super().__init__)
        self.hp = hp

    def sign_in(self):
        print('player sign in')


class Worrior(Player):
    def __init__(self, worrior_name, power, **kwargs):
        super().__init__(**kwargs)
        self.name = worrior_name
        self.power = power

    def __str__(self):
        return "The worrior's name: " f'{self.name} \n' \
               "He has the power:" f'{self.power}'


class Archer(Player):
    def __init__(self, archer_name, agility, **kwargs):
        super().__init__(**kwargs)
        self.name = archer_name
        self.agility = agility

    def __str__(self):
        return "The archer's name: " f'{self.name} \n' \
               "He has the agility:" f'{self.agility}'


class Boss(Worrior, Archer):
    def __init__(self, name, power, agility, hp):
        super().__init__(archer_name=name, worrior_name=name, power=power, agility=agility, hp=hp)

    def __str__(self):
        return "The boss's name: " f'{self.name} \n' \
               "With Worrior's power " f'{self.power} \n' \
               "and With Archer's agilit" f'{self.agility}' \
               "The boss' hp is: " f'{self.hp}'
1 голос
/ 07 августа 2020

Это происходит из-за порядка разрешения методов (MRO) в новых классах стилей. MRO класса Boss -

ipdb> Boss.mro()                                                                                                                   
[<class '__main__.Boss'>, <class '__main__.Worrior'>, <class '__main__.Archer'>, <class '__main__.Player'>, <class 'object'>]
ipdb>  

Это означает, что вызовы super () в классе Worrior на самом деле относятся к Archer, а не Player.

Вы можете обратиться к этому сообщению о переполнении стека - Порядок разрешения методов (MRO) в классах нового стиля? , который хорошо объясняет, как MRO работает в python.

0 голосов
/ 07 августа 2020

Похоже, что Python решает снова пройти всю иерархию, когда достигает super() в классе Воина (я исправил ваше написание, это не «Воин»).

I ' m не уверен, в чем вы ошибаетесь, или это просто ситуация, когда вы не можете использовать super(). Я ожидал того же, что и вы, и тоже был удивлен результатом.

Однако в приведенном ниже коде проблем нет, хотя вы, конечно, теряете гибкость, и мне не нравится делать это :

class Player:
    def __init__(self, hp):
        self.hp = hp

    def sign_in(self):
        print('player sign in')


class Warrior(Player):
    def __init__(self, name, power, hp):
        Player.__init__(self, hp)
        self.name = name
        self.power = power

    def __str__(self):
        return "The warrior's name: " f'{self.name} \n' \
            "He has the power:" f'{self.power}'


class Archer(Player):
    def __init__(self, name, agility, hp):
        Player.__init__(self, hp)
        self.name = name
        self.agility = agility

    def __str__(self):
        return "The archer's name: " f'{self.name} \n' \
            "He has the agility:" f'{self.agility}'


class Boss(Warrior, Archer):
    def __init__(self, name, power, agility, hp):
        Warrior.__init__(self, name, power, hp)
        Archer.__init__(self, name, agility, hp)

    def __str__(self):
        return "The boss's name: " f'{self.name} \n' \
            "With Warrior's power: " f'{self.power} \n' \
            "and With Archer's agility: " f'{self.agility}'\
            "The boss' HP is: " f'{self.hp}'


boss = Boss("Boss", 50, 50, 100)
print(boss.__str__())

Итак, не совсем полный ответ, но я хотел дать обратную связь - надеюсь, кто-то другой предоставит полный ответ с объяснением того, что на самом деле происходит.

...