You are not logged in.

#1 2010-12-04 16:31:27

firecat53
Member
From: Lake Stevens, WA, USA
Registered: 2007-05-14
Posts: 1,542
Website

[SOLVED]Python Class/Subclass question

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

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:

>>> AudioFile('test.ogg').play()
Playing ogg

However 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 mp3

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

Last edited by firecat53 (2010-12-07 05:28:25)

Offline

#2 2010-12-04 17:11:15

ryuslash
Member
Registered: 2010-10-11
Posts: 58
Website

Re: [SOLVED]Python Class/Subclass question

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

#3 2010-12-04 17:44:41

firecat53
Member
From: Lake Stevens, WA, USA
Registered: 2007-05-14
Posts: 1,542
Website

Re: [SOLVED]Python Class/Subclass question

ryuslash wrote:

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

#4 2010-12-04 18:08:11

linkmaster03
Member
Registered: 2008-12-27
Posts: 269

Re: [SOLVED]Python Class/Subclass question

I would make a separate wrapper class/function that handles the logic and returns the appropriate object.

Offline

#5 2010-12-04 18:12:40

kachelaqa
Member
Registered: 2010-09-26
Posts: 216

Re: [SOLVED]Python Class/Subclass question

firecat53 wrote:

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

#6 2010-12-04 19:59:42

cactus
Taco Eater
From: t͈̫̹ͨa͖͕͎̱͈ͨ͆ć̥̖̝o̫̫̼s͈̭̱̞͍̃!̰
Registered: 2004-05-25
Posts: 4,622
Website

Re: [SOLVED]Python Class/Subclass question

firecat53 wrote:

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

#7 2010-12-04 20:27:23

firecat53
Member
From: Lake Stevens, WA, USA
Registered: 2007-05-14
Posts: 1,542
Website

Re: [SOLVED]Python Class/Subclass question

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 smile 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 smile )

Last edited by firecat53 (2010-12-04 20:45:43)

Offline

#8 2010-12-04 20:28:17

kachelaqa
Member
Registered: 2010-09-26
Posts: 216

Re: [SOLVED]Python Class/Subclass question

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?

Offline

#9 2010-12-04 20:37:49

drcouzelis
Member
From: Connecticut, USA
Registered: 2009-11-09
Posts: 4,092
Website

Re: [SOLVED]Python Class/Subclass question

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()
firecat53 wrote:

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

#10 2010-12-04 20:51:26

the_isz
Member
Registered: 2009-04-14
Posts: 280

Re: [SOLVED]Python Class/Subclass question

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 smile

I hope I helped you understand it more than I confused you with another
language... Have fun!

Offline

#11 2010-12-04 21:05:35

kachelaqa
Member
Registered: 2010-09-26
Posts: 216

Re: [SOLVED]Python Class/Subclass question

drcouzelis wrote:

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

#12 2010-12-04 22:20:59

cactus
Taco Eater
From: t͈̫̹ͨa͖͕͎̱͈ͨ͆ć̥̖̝o̫̫̼s͈̭̱̞͍̃!̰
Registered: 2004-05-25
Posts: 4,622
Website

Re: [SOLVED]Python Class/Subclass question

kachelaqa wrote:
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.
smile


"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

#13 2010-12-05 06:12:04

Substrata
Member
Registered: 2010-11-09
Posts: 3

Re: [SOLVED]Python Class/Subclass question

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

#14 2010-12-07 05:29:54

firecat53
Member
From: Lake Stevens, WA, USA
Registered: 2007-05-14
Posts: 1,542
Website

Re: [SOLVED]Python Class/Subclass question

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

#15 2010-12-07 13:57:51

Stebalien
Member
Registered: 2010-04-27
Posts: 1,239
Website

Re: [SOLVED]Python Class/Subclass question

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

Steven [ web : git ]
GPG:  327B 20CE 21EA 68CF A7748675 7C92 3221 5899 410C

Offline

#16 2010-12-07 15:01:15

firecat53
Member
From: Lake Stevens, WA, USA
Registered: 2007-05-14
Posts: 1,542
Website

Re: [SOLVED]Python Class/Subclass question

Nice!

Scott

Offline

#17 2011-09-25 13:48:37

aspidites
Member
Registered: 2011-03-11
Posts: 30

Re: [SOLVED]Python Class/Subclass question

Stebalien wrote:

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" % ext

Or, 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" % ext

note: 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

Board footer

Powered by FluxBB