【问题标题】:Multiple inheritance with super() in Python在 Python 中使用 super() 进行多重继承
【发布时间】:2020-11-27 10:14:15
【问题描述】:

我在 Python 中练习多重继承。 没有Boss 类,一切顺利。非常感谢任何帮助。 我提到过:How does Python's super() work with multiple inheritance?

反馈:

---------------------------------------------------------------------------
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类里面的power属性,然后就停了。

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__())

【问题讨论】:

  • @ThierryLathuille 解决了订单问题,这是一篇非常好的帖子,但它没有解决如何处理构造函数的签名从类到类的变化(就像这里一样)

标签: python multiple-inheritance super


【解决方案1】:

来自@Thierry Lathuille 的link 是正确的阅读方式,但我会尝试添加一些额外的解释。初始化器的 MRO 是 [Boss, Worrior, Archer, Player]。这意味着(有点令人困惑)当 Worrior 调用 super() 时,这实际上是指 Archer,而不是 Player。如果您在每次调用该方法之前放置print(super().__init__),您会在崩溃之前找到这样的输出:

<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 中多重继承的主要缺陷,我通常建议不要这样做,除非你有零参数初始化程序。

如果您尝试显式调用每个基类初始化程序(例如Player.__init__),那么最终您的一些初始化程序会执行多次。

为了能够传递您的论点,您需要利用 **kwargs。此外,随着这些参数的传递,每个唯一的参数名称都将从 kwargs 中删除。所以这意味着你不能重用name 作为初始化参数。我将改用worrior_namearcher_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}'

【讨论】:

    【解决方案2】:

    这是因为新样式类中的方法解析顺序(MRO)。 Boss级的MRO——

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

    这意味着 Worrior 类中的 super() 调用实际上是指 Archer,而不是 Player。

    您可以参考这篇堆栈溢出帖子 - Method Resolution Order (MRO) in new-style classes?,它很好地解释了 MRO 在 python 中的工作原理。

    【讨论】:

      【解决方案3】:

      当 Python 到达 Warrior 类中的 super() 时,它似乎决定再次遍历整个层次结构(我已经更正了你的拼写,它不是 'Worrior')。

      我不确定您哪里出错了,或者这是否只是您无法使用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__())
      

      所以,并不是一个完整的答案,而是想提供反馈 - 希望其他人提供一个完整的答案并解释真正发生的事情。

      【讨论】:

      • 感谢您查看代码@Grismar。实际上,在我看来,它是安全且结构化的,没有多重继承。
      猜你喜欢
      • 2020-05-15
      • 1970-01-01
      • 2016-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-20
      • 1970-01-01
      • 2020-07-05
      相关资源
      最近更新 更多