[Tutor] Dumb Subclassing question

Andy Baker andy at andybak.net
Thu Aug 5 16:09:58 CEST 2004

> > I, like everyone else in the universe, am writing a prog to
> manipulate and
> > organize mp3's...
> Nope, I just use iTunes :-)

Oooh I wasn't going to bite but I will anyway! I have been using a mix of
'MP3 Tag Studio' and Musicbrainz which between them are a fair bit more
powerful than Itunes but I need more power!

> > So I subclass the 'path' object as 'mp3path' and add some of my own
> methods.
> Sounds OK.
> > I have added a new method ID3tag to my MP3path class I want 
> > myMP3path.files to return a list of objects that can still
> access my
> > ID3tag method.
> In that case they need to have access to objects of your class.
> Or do you mean you want other objects to access the ID3tag 
> method of the returned objects? ie the returned objects 
> should have ID3tag methods?

I mean that any path object returned should have mp3 related methods.

> > Options that have occurred to me:
> >
> > 1. Override every method in 'path' that returns paths with a wrapper
> method
> > that converts them into mp3paths. This seems ugly, boring and
> pointless.
> Its the only sensible way if you want all path objects to be 
> your specialised variants. How else can the path objects be 
> treated as your objects unless they get converted at some 
> stage? But see below...
> > 3. Add methods dynamically into the path object. (Is this
> 'decorator'?)
> No its not decorator and is even more messy!

(unrelated question: How does adding methods to existing instances differ
from decorator then? I get the feeling that a lot of common design patterns
don't apply exactly the same way to Python because it is less restrictive of
what you can do in the first place)

> > looked at the instancemethod function in the standard library 'new'
> module
> > and this adds methiods to instances but not to classes so I would
> have to do
> > this for every instance.
> Exactly so. Yuk!
Well! You say Yuk but I did a bit more digging and (thanks to Mark
is ) found that despite what is implied in the standard library docs,
'instancemethod' will happily add methods to classes. This new methods then
get nicely inherited by instances of the class.

So I end up with code like this:

from path import *
import new, id3
# Methods to add to 'path' class
def _mp3s(self):
    return [mp3s for mp3s in self.files('*.mp3')]
def _hasmp3s(self):
    return (self.isdir() and len(self.mp3s())>0)
# Bind new methods to add to 'path' class
path.mp3s = new.instancemethod(_mp3s, None, path)
path.hasmp3s = new.instancemethod(_hasmp3s, None, path)

testmp3 = path('testmp3')
first_directory = testmp3.dirs()[0]
print first_directory.hasmp3s()

_____________________ (Obviously the code don't do much yet!)

This seems cleaner to me than the alternative of overriding all the methods
just to add a type wrapper. I'd be interested to know what trouble I might
be storing up in terms of bad OO practice...

> > 4. Forget the whole OO thang and just use functions. (Looking more 
> > attractive by the minute ;-)
> How would that help? You still have the same issues?
> One thing you might like to consider is the composite pattern.
> Here we distinguish between link nodes and leafnodes of a tree.
> You only want path nodes to gave ID3tags methods if they are 
> leaf nodes(ie files) I assume?
> So you can write a function that determines the nature of 
> path node you get and only convert the leaf nodes to your 
> class type. That is instead of overriding path to retirn your 
> objects why not get the list of paths back and then convert 
> those as needed to your class?
Well, the class I am basing myself on doesn't differentiate between path's
and filenames and I find that approach fairly nice (only one object type to
deal with. Just use .isfile() or isdir() if you need to know).

> Does that make sense - I don't think I'm explaining it very 
> well... And don't have time for an example!
> Basically write a type conversion function (like int(),
> str(),list() etc) and apply it as needed.
> Pseudo code:
> def mp3path(aVanillaPath):
>     mp3p = MP3Path()     # create new instance
>     for attribute in aVanillaPath:  # use getattr()???
>         mp3p.attribute = attribute   # copy inherited stuff
>     return mp3p
> for path in myPath.files():
>     if path is leaf node  # using isinstance or somesuch?
>        path = mp3path(path)
>        path.ID3tag()      # do things with new path object
> Alan G.

More information about the Tutor mailing list