假设我有两个类,如Mp3Player
和DVDPlayer
,我将创建一个继承前两个类的新类MultiPlayer
。
Mp3Player
和DVDPlayer
都有一个具有相同签名的方法:
class MP3Player:
def play(self, content):
print(f'MP3 player is playing {content}')
class DVDPlayer:
def play(self, content):
print(f'DVD player is playing {content}')
我想覆盖MultiPlayer
中的play
方法,并且我希望能够根据某些条件调用相应的超类。
class MultiPlayer(MP3Player, DVDPlayer):
def play(self, content):
if mp3_condition:
# some how call the play method in MP3Player
elif dvd_condition:
# some how call the play method in DVDPlayer
else:
print('the given content is not supported')
我不能使用super().play(content)
,因为基于 MRO 规则,它总是解析为MP3Player
中的play
方法。
做这种事情的pythonic方法是什么?
当你使用继承时,你说子类是父类的一种类型,只是一个更专业的类型。 这称为is-a关系。
一个常见的例子用动物来说明这一点。想象一下,你有三个类:动物、猫和狮子。 狮子是猫,猫是动物,所以在这种情况下使用继承是有意义的。
但是,您的情况有所不同。 你有一个多人游戏类,通过使用继承,你说它是一个MP3播放器,它也是一个DVD播放器。
这可以工作,但是在这种情况下使用组合而不是继承更自然。 组合是一种has-a关系而不是is-a,这意味着您的 MultiPlayer 类内部有一个MP3 播放器,并且它内部还有一个 DVD 播放器,但从根本上说,它不是这两件事。
我会显式调用play
方法:
class MultiPlayer(MP3Player, DVDPlayer):
def play(self, content):
if mp3_condition:
MP3Player.play(self, content)
elif dvd_condition:
DVDPlayer.play(self, content)
else:
print('the given content is not supported')
注意— 如果你绝对想使用super()
,你可以这样做:
class MultiPlayer(MP3Player, DVDPlayer):
def play(self, content):
if mp3_condition:
super().play(content)
elif dvd_condition:
super(MP3Player, self).play(content)
else:
print('the given content is not supported')
但我会避免它,因为它假设在MP3Player
和MP3Player
和DVDPlayer
的共同祖先之间没有具有play
方法的类(也就是说object
这里(。如果以后你改变主意并引入这样一个类,super(MP3Player, self).play(content)
将调用这个类的play
方法,而不是你打算的DVDPlayer.play
方法。 类永远不应该在其基类的层次结构中假定任何内容。
此外,super(MP3Player, self).play(content)
比DVDPlayer.play(self, content)
更难理解,并且还需要显式的类名。因此,您只能获得松散的灵活性和清晰度。
为了更好地理解super()
在 Python 中是如何工作的,我强烈推荐 Raymond Hettinger 的优秀文章Python 的 super(( 被认为是 super!