You are not logged in.
Ok, I'm still pretty new at OOP in general, and OOP with Python in particular. There was a section on Polymorphism in Dusty's most excellent book, Python 3 OOP that discussed playing audio files with a class structure like:
class AudioFile:
def __init__(self, filename):
if not filename.endswith(self.ext):
raise Exception("Invalid format")
self.filename=filename
class Mp3File(AudioFile):
ext = 'mp3'
def play(self):
print "Playing mp3"
class OggFile(AudioFile):
ext = 'ogg'
def play(self):
print "Playing ogg"
.........Then you can say:
>>> oggfile = OggFile('test.ogg')
>>>oggfile.play()
Playing oggOk, I get how that all works with inheritance & polymorphism, but I thought that the point was to be able to do this so you don't have to specifically call the subclass, but let the superclass take care of calling the correct subclass like:
>>> AudioFile('test.ogg').play()
Playing oggHowever this fails:
>>> AudioFile('test.ogg').play()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
AttributeError: AudioFile instance has no attribute 'ext'So, my question is, is there a way to accomplish that goal of switching subclasses automatically with resorting to something like:
class AudioFile:
exts = {'mp3': Mp3File, 'ogg': OggFile}
def __init__(self, file):
self.filename = file
self.command = AudioFile.exts[os.path.splitext(file)]
def __call__(self):
self.command(self.filename).play()
class Mp3File:
def __init__(self,filename):
self.filename = filename
def play(self):
print "Playing mp3"
>>>AudioFile('test.mp3')()
Playing mp3It doesn't quite feel right and it seems to throw out any benefits of inheritance, although it does retain the polymorphism (I think). I hope my question makes sense!
Thanks,
Scott
Last edited by firecat53 (2010-12-07 05:28:25)
Offline
inheritance is more about (among other things) overwriting the behavior of the class you inherit from. Not really extending the class as you seem to be trying.
With inheritance you can declare a new function that will be called when the parent class's function is called.
So you have a function that takes an Audiofile as a parameter and you give it an Mp3File object, it calls the play() function and instead of the audiofile's function it gets the mp3file's.
Also, the error you got has more to do with the fact that you haven't defined an ext variable in AudioFile and your init is checking for it.
Offline
So you have a function that takes an Audiofile as a parameter and you give it an Mp3File object, it calls the play() function and instead of the audiofile's function it gets the mp3file's.
Also, the error you got has more to do with the fact that you haven't defined an ext variable in AudioFile and your init is checking for it.
The whole point of the original code was to NOT have ext defined in the parent class, but automatically detected from the subclass. The question is how to automatically call the correct subclass based on the file extension. If, like you said, you need to pass an Mp3File object to a function, you've taken that detection out of the AudioFile class and are saying that something else is determining what the appropriate subclass to call will be, right? And I'm trying to put that logic INTO the AudioFile class, so you can just pass the file name to the class and it knows which subclass method to execute.
Thanks,
Scott
Offline
I would make a separate wrapper class/function that handles the logic and returns the appropriate object.
Offline
Ok, I'm still pretty new at OOP in general, and OOP with Python in particular. There was a section on Polymorphism in Dusty's most excellent book, Python 3 OOP that discussed playing audio files with a class structure like:
class AudioFile: def __init__(self, filename): if not filename.endswith(self.ext): raise Exception("Invalid format") self.filename=filename class Mp3File(AudioFile): ext = 'mp3' def play(self): print "Playing mp3" class OggFile(AudioFile): ext = 'ogg' def play(self): print "Playing ogg" .........Then you can say:
>>> oggfile = OggFile('test.ogg') >>>oggfile.play() Playing ogg
as it stands, this example makes no sense.
if the file extension is not known beforehand, there is no way to determine which subclass to use.
on the other hand, if the extension is known beforehand, there is no need to check it...
one of the main benefits of subclassing is code re-use. the AudioFile class should contain a lot of generic methods and default attribute values which are shared between all subclasses. the individual subclasses will then over-ride any methods and attributes which need special handling.
but note that, by definition, a generic AudioFile base class should not know anything specific about any of it's potential subclasses.
this implies that separate code is required for selecting the right subclass to use for a given file extension. so you might have a dictionary that maps file extensions to subclasses, and a factory function that is responsible for creating instances of those subclasses.
Offline
Ok, I'm still pretty new at OOP in general, and OOP with Python in particular
......*snip*....Ok, I get how that all works with inheritance & polymorphism, but I thought that the point was to be able to do this so you don't have to specifically call the subclass, but let the superclass take care of calling the correct subclass like
... *snip*...So, my question is, is there a way to accomplish that goal of switching subclasses automatically with resorting to something like:
......*snip*....It doesn't quite feel right and it seems to throw out any benefits of inheritance, although it does retain the polymorphism (I think). I hope my question makes sense!
Thanks,
Scott
Class hierarchies are upward looking, not downward looking. Eg. the leaves know what their parents are, but the parents might not know about the leaf classes. (you _can_ get this with 'clever' class method callables but I consider it a questionable practice best reserved for never, or exceedingly rare situations). Anyway...
As others in this thread have suggested so far, you don't really want to be using AudioFile directly. The AudioFile class should contain behaviors common to all subclasses, and represent as generic an interface as possible. Based on how the class is structured, you would never create an AudioFile object directly (since it has no self.ext variable). It is merely a common interface.
So in the code sample provided, you would be better served having a ... takes a deep breath .... factory type class or module level method generate the appropriate object for you based on filename detection. *washed mouth out with soap for saying factory*
For example:
def AudioFileLoader(filename):
supported_types = {'ogg': OggFile, 'mp3': Mp3File}
ext = filename.rsplit('.')[1]
if ext in supported_types:
return supported_types[ext](filename)
else:
return None>>> AudioFileLoader('test.mp3')
<__main__.Mp3File instance at 0x1004d2a28>
>>> AudioFileLoader('test.ogg')
<__main__.OggFile instance at 0x1004d2ab8>
>>> AudioFileLoader('test.foo')
>>> Last edited by cactus (2010-12-04 20:00:53)
"Be conservative in what you send; be liberal in what you accept." -- Postel's Law
"tacos" -- Cactus' Law
"t̥͍͎̪̪͗a̴̻̩͈͚ͨc̠o̩̙͈ͫͅs͙͎̙͊ ͔͇̫̜t͎̳̀a̜̞̗ͩc̗͍͚o̲̯̿s̖̣̤̙͌ ̖̜̈ț̰̫͓ạ̪͖̳c̲͎͕̰̯̃̈o͉ͅs̪ͪ ̜̻̖̜͕" -- -̖͚̫̙̓-̺̠͇ͤ̃ ̜̪̜ͯZ͔̗̭̞ͪA̝͈̙͖̩L͉̠̺͓G̙̞̦͖O̳̗͍
Offline
Thanks all for your help!! I even read the section on factories in Dusty's book...apparently I need several more readings for all of it to stick
It looks like the factory concept was what I needed.
@cactus: Now, I do have to ask...what was with the, ummmm, negative?(*washed mouth out with soap for saying factory*) connotations around factories? Is there a better way to design that avoids them? Or....??
Thanks!
Scott
PS -- here's a link to a stack overflow discussion of much the same issue (found after being reminded about factories
)
Last edited by firecat53 (2010-12-04 20:45:43)
Offline
So in the code sample provided, you would be better served having a ... takes a deep breath .... factory type class or module level method generate the appropriate object for you based on filename detection. *washed mouth out with soap for saying factory*
just curious, but:
"factory function" is exactly the right term to use in this context.
so what's your objection to using it?
Offline
As many have mentioned, your example doesn't make much sense in terms of inheritance and polymorphism.
Here's a simple example of why inheritance and polymorphism kick butt:
playlist = [Mp3File('song1.mp3'), OggFile('song2.ogg'), Mp3File('song3.mp3'), OggFile('song4.ogg')]
for song in playlist:
song.play()So, my question is, is there a way to accomplish that goal of switching subclasses...
No, if you really wanted to do that, I think you would pretty much write that part by using "brute force".
Polymorphism is special because, after you initialize an object as the subclass, you can treat it the rest of the time as if it was the superclass.
Offline
I think python is not a great choice of programming language to explain
polymorphism. Not that it doesn't support it or doesn't make heavy use of it...
It's just not very visible.
If I may take you on a small journey into C++:
class AudioFile
{
public:
AudioFile (std::string filename, std::string ext)
: _filename(filename)
, _ext(ext)
{
if (filename.rfind(ext) != filename.size() - ext.size())
// Throwing exceptions inside a constructor is not a good idea, but I'll
// keep it for this example.
throw std::invalid_argument("Invalid extension for filename.");
}
virtual void play () = 0;
protected:
std::string _filename;
std::string _ext;
};
class Mp3File : public AudioFile
{
public:
Mp3File (std::string filename)
: AudioFile(filename, "mp3") {}
virtual void play ()
{
std::cout << "Playing mp3" << std::endl;
}
};
class OggFile : public AudioFile
{
public:
OggFile (std::string filename)
: AudioFile(filename, "ogg") {}
virtual void play ()
{
std::cout << "Playing ogg" << std::endl;
}
};This code essentially represents your given example in C++. Now, why did I give
you that?
class AudioFile
{
[...]
virtual void play () = 0;
[...]
};Declaring a function pure virtual means that it doesn't have a definition, only
a declaration. This means, that all AudioFiles have a function play that can be
called on them, the base class AudioFile itself doesn't have an implementation
for it however. It needs to be provided by the respective subclass, which is
done for both Mp3File and OggFile.
What you can do now (and what shows what polymorphism is (among other things)
about) is:
AudioFile* audioFile = new Mp3File("hello.mp3");
audioFile->play();First, you allocate a new Mp3File, but use a pointer to the base class to refer
to it. This is legal, because every Mp3File is-a AudioFile.
Then you call the function play on a pointer of AudioFile, although AudioFile
doesn't actually have a definition for it! Because the object behind the pointer
is an Mp3File, which provides such a definition, this works and prints "Playing
mp3".
Now you can imagine having dozens of subclasses of AudioFile and maybe a class
PlayList, which collects AudioFiles of arbitrary formats and plays them one
after another. PlayList wouldn't need to know what format each AudioFile has to
play them:
class PlayList
{
public:
PlayList () {}
void addToList (AudioFile* audioFile)
{
_audioFiles.push_back(audioFile);
}
void playNext ()
{
if (_audioFiles.size() < 1)
return;
_audioFiles.front()->play();
_audioFiles.erase(_audioFiles.begin());
}
private:
std::vector<AudioFile*> _audioFiles;
};As you can see, PlayList isn't interested in the Mp3File or OggFile classes in
any way, even though it might be using them implicitly. This is polymorphism
live ![]()
I hope I helped you understand it more than I confused you with another
language... Have fun!
Offline
Polymorphism is special because, after you initialize an object as the subclass, you can treat it the rest of the time as if it was the superclass.
but also note that these same benefits can be achieved without any inheritance at all.
this is known as "duck-typing" - what matters most is what an object can do; not what it is.
Offline
cactus wrote:So in the code sample provided, you would be better served having a ... takes a deep breath .... factory type class or module level method generate the appropriate object for you based on filename detection. *washed mouth out with soap for saying factory*
just curious, but:
"factory function" is exactly the right term to use in this context.
so what's your objection to using it?
Call it a reflexive action based on historical bias. Lots of python-twisted and java, with factoryfactories and other obtuse (but occasionally useful) constructs.![]()
"Be conservative in what you send; be liberal in what you accept." -- Postel's Law
"tacos" -- Cactus' Law
"t̥͍͎̪̪͗a̴̻̩͈͚ͨc̠o̩̙͈ͫͅs͙͎̙͊ ͔͇̫̜t͎̳̀a̜̞̗ͩc̗͍͚o̲̯̿s̖̣̤̙͌ ̖̜̈ț̰̫͓ạ̪͖̳c̲͎͕̰̯̃̈o͉ͅs̪ͪ ̜̻̖̜͕" -- -̖͚̫̙̓-̺̠͇ͤ̃ ̜̪̜ͯZ͔̗̭̞ͪA̝͈̙͖̩L͉̠̺͓G̙̞̦͖O̳̗͍
Offline
As others have noted, make a factory function. You could look into Python's runtime introspection, as well. Eg, the factory can use introspection on the base class to find the subclasses and, given some file-extension method in your class tree, match the filename to an appropriate subclass.
Offline
Factories seem to be the appropriate solution for solving this. This is exactly why I love this community! Thanks very much for all of the thoughtful and informative comments!!
Scott
Offline
A better way to do this (no precoded list of supported filetypes):
def AudioFileLoader(filename):
ext = filename.rsplit('.')[1]
module = __import__(self.__module__)
try:
return getattr(module, "%sFile" % ext.capitalize() )(filename)
except AttributeError:
return NoneOffline
A better way to do this (no precoded list of supported filetypes):
def AudioFileLoader(filename): ext = filename.rsplit('.')[1] module = __import__(self.__module__) try: return getattr(module, "%sFile" % ext.capitalize() )(filename) except AttributeError: return None
Except this code doesn't work as is. You would have to add self to the arguments list and also make it a static method. Thus, I would say catcus' example is superior.
If we make AudioFile a new style class however (inheriting from object), we could do the following:
def AudioFileLoader(filename):
ext = filename.rsplit('.')[1]
audio_file_types = dict((subclass.ext, subclass) for subclass in AudioFile.__subclasses__())
try:
return audio_file_types[ext](filename)
except KeyError:
print "%s extension not supported" % extOr, a more verbose, but easier to understand version:
def AudioFileLoader(filename):
ext = filename.rsplit('.')[1]
audio_file_types = {}
for subclass in AudioFile.__subclasses__():
audio_file_types[subclass.ext] = subclass
try:
return audio_file_types[ext](filename)
except KeyError:
print "%s extension not supported" % extnote: have preferred to follow PEP convention and named the function audio_file_loader, but I didn't want to break the precedent set in earlier posts.
note 2: I would have wrapped the dict() line, except that wrapping in these forums is a bit awkward
Last edited by aspidites (2011-09-25 13:52:34)
coder formally known as EnvoyRising
Offline