python对象-多态
调用不同的子类将会产生不同的行为,而无须明确知道这个子类实际上是什么。
比如,在一个可以播放音频文件的程序中,媒体播放器可能需要加载一个AudioFile对象然后play它,我们把一个play()的方法放在这个对象里,它负责解压或者提取音频,然后把音频引导到声卡或者扬声器,一个AudioFile行为可以如下这么简单:
audio_file.play()
然而,对于不同类型的文件,解压和提取音频文件的过程是很不一样的。.wav文件存储为压缩的音频,.mp3、.wma和.ogg文件都有不同的压缩算法。
我们可以使用多态和继承来让设计简单化。每种不同类型的文件都可以表示为一个AudioFile的不同子类,例如WavFile、MP3File。每一种都有一个play()方法。媒体播放器对象永远不需要知道它引用了AudioFile的哪一个子类,它只是调用play()方法然后多态地让对象去处理实际播放的细节。
class AudioFile: def __init__(self, filename): if not filename.endswith(self.ext): raise Exception("Invalid file format") self.filename = filename class MP3File(AudioFile): ext = "mp3" def play(self): print("playing {} as mp3".format(self.filename)) class WavFile(AudioFile): ext = "wav" def play(self): print("playing {} as wav".format(self.filename)) class OggFile(AudioFile): ext = "ogg" def play(self): print("playing {} as ogg".format(self.filename))
所有音频文件的检查确保了初始化的一个有效扩展,但是注意到,如何让父类的__init__方法取访问来自不同子类的ext变量,这就是多态的工作。如果文件并没有以正确的名字结尾,就会抛出一个异常。事实上,AudioFile没有存储ext变量的引用这一事实并不能阻止它在子类中访问。
每一个AudioFile子类会以不同的方式实现play()方法,媒体播放器可以使用完全相同的代码来播放文件,无论它是什么类型,它并不在乎使用AudioFile的哪一个子类。
>>> ogg = OggFile("myfile.ogg") >>> ogg.play() playing myfile.ogg as ogg >>> mp3 = MP3File("myfile.mp3") >>> mp3.play() playing myfile.mp3 as mp3 >>> not_an_mp3 = MP3File("myfile.ogg") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "AudioFile.py", line 4, in __init__ raise Exception("Invalid file format") Exception: Invalid file format
以上的例子展现了AudioFile.__init__方法在不知道哪一个子类在引用它的情况下,可以检查文件类型。
参考:
1、《Python3 面向对象编程》 [加]Dusty Philips 著