调用不同的子类将会产生不同的行为,而无须明确知道这个子类实际上是什么。
比如,在一个可以播放音频文件的程序中,媒体播放器可能需要加载一个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 = filenameclass 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 "", line 1, in File "AudioFile.py", line 4, in __init__ raise Exception("Invalid file format")Exception: Invalid file format
以上的例子展现了AudioFile.__init__方法在不知道哪一个子类在引用它的情况下,可以检查文件类型。
参考:
1、《Python3 面向对象编程》 [加]Dusty Philips 著