Abstract class

Stephen Horne sh006d3592 at blueyonder.co.uk
Sun Sep 14 15:49:24 EDT 2008


On Sun, 14 Sep 2008 18:03:23 +0200, Mr.SpOOn <mr.spoon21 at gmail.com>
wrote:

>I have to manage many elements of music such as notes, intervals,
>scales, chords and so on. All these elements share properties and
>behavior, so what I want to do is an abstract class "Note" and other
>subclasses, for example "NaturalNote", "FlatNote", "SharpNote" etc.

First off, one approach in Python is to not have a base class at all.
You don't need a base class just to implement a shared interface,
basically. If it walks like a duck and quacks like a duck, you can
usually treat it like a duck, as I think the Ruby crowd say.

Second, it's possible to overdo object orientation, which is probably
Roy Smiths point.

Sometimes, the easiest thing to do is to use a simple integer
representation of your values. With dates, you'd probably use a Julian
day number. That's what spreadsheets usually do, for instance.

With music notes, you'd probably use the note numbers that MIDI uses.

http://www.harmony-central.com/MIDI/Doc/table2.html

You can always extend the range beyond those notes that MIDI supports
if needed.

Doing this means you can switch octaves or transpose using simple
additions or subtractions, for instance. Notes can be decoded using
rounded integer division to find the octave number and the
remainder/modulo % operator (plus maybe a look-up table) to identify
the note.

Use // for integer division (rounded toward -infinity IIRC) or you
risk problems going from Python 2 to Python 3 - the semantics for the
/ division operator are changing. Up until now it would round integer
divisions, but in future it will be considered a "true" non-rounding
division.

This may not work, though, if you aren't working with the
even-tempered scale (if I got that right). There are times when
C-sharp is not the same as D-flat, as MIDI assumes.

You may want to wrap the value in a class, but as far as possible all
your calculations would be based on the integers. Chords might be
represented using either a list or a set - my Python's a little rusty
ATM but IIRC there's a set type based on a dictionary. If you're a
bit-twiddling fan, you might even use a bitvector approach (I don't
think theres a specialised set-of-unsigned-integers bit-vector class
in the library, but I could be wrong). But for most purposes, you'd
probably want the spec. for the chord - "C major" rather than "C, E,
G".

In situations like this, it's sometimes useful to have simple
functions in a module, or else a 'tool' class (which works on the
values, but only encapsulates the tools own state). You can still wrap
the values in a class with a common interface. The point of this is
that it's relatively easy to share functionality when you implement
some value-wrapper classes in several different ways - e.g. note-set
chords vs. specification-based chords, note-number representations vs.
string representations, etc.

That is, in object oriented theory, the object manipulates itself -
but that doesn't always work so well in practice, especially for
functions that deal with two or more objects. As in the real world, a
tool sometimes works better. And if the tool doesn't need any state,
it may as well be a single procedure/function in a module rather than
a class.




More information about the Python-list mailing list