Python 中 super() 的多重继承
Multiple inheritance with super() in Python
我在Python练习多重继承。
没有 Boss
class,一切顺利。非常感谢任何帮助。
我提到了: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
class里面拿了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__())
看来 Python 到达 Warrior super()
时决定再次遍历整个层次结构 class(我已经更正了你的拼写,不是 '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__())
所以,不是一个完整的答案,但想提供反馈 - 希望其他人提供一个完整的答案并解释真正发生的事情。
来自@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 中多重继承的主要缺陷,我通常建议不要这样做,除非你有 zero-argument 个初始化程序。
如果您尝试显式调用每个基础 class 初始化程序(例如 Player.__init__
),那么您最终会执行多次初始化程序。
为了能够传递你的论点,你需要利用 **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}'
发生这种情况是因为新样式 classes 中的方法解析顺序 (MRO)
。
Boss的MRO class -
ipdb> Boss.mro()
[<class '__main__.Boss'>, <class '__main__.Worrior'>, <class '__main__.Archer'>, <class '__main__.Player'>, <class 'object'>]
ipdb>
这意味着 Worrior class 中的 super() 调用实际上指的是 Archer,而不是 Player。
您可以参考这个堆栈溢出 post - Method Resolution Order (MRO) in new-style classes?,它很好地解释了 MRO 在 python 中的工作原理。
我在Python练习多重继承。
没有 Boss
class,一切顺利。非常感谢任何帮助。
我提到了: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
class里面拿了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__())
看来 Python 到达 Warrior super()
时决定再次遍历整个层次结构 class(我已经更正了你的拼写,不是 '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__())
所以,不是一个完整的答案,但想提供反馈 - 希望其他人提供一个完整的答案并解释真正发生的事情。
来自@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 中多重继承的主要缺陷,我通常建议不要这样做,除非你有 zero-argument 个初始化程序。
如果您尝试显式调用每个基础 class 初始化程序(例如 Player.__init__
),那么您最终会执行多次初始化程序。
为了能够传递你的论点,你需要利用 **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}'
发生这种情况是因为新样式 classes 中的方法解析顺序 (MRO)
。
Boss的MRO class -
ipdb> Boss.mro()
[<class '__main__.Boss'>, <class '__main__.Worrior'>, <class '__main__.Archer'>, <class '__main__.Player'>, <class 'object'>]
ipdb>
这意味着 Worrior class 中的 super() 调用实际上指的是 Archer,而不是 Player。
您可以参考这个堆栈溢出 post - Method Resolution Order (MRO) in new-style classes?,它很好地解释了 MRO 在 python 中的工作原理。