Abstract class

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Mr.SpOOn

    Abstract class

    Hi,
    I'm going to work on a project to represent some musical theory in
    Python, in an object oriented way.

    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 "NaturalNot e", "FlatNote", "SharpNote" etc.

    The idea is not original, I read it in some papers where they talk
    about an implementation in smalltalk.

    I want to use Python (of course) and I'd like to know what is the
    practice in such a case. I mean, in python there aren't abstract
    classes, but I read about some way to emulate the same behavior.

    What do you suggest me?

    Thanks,
    Carlo
  • Larry Bates

    #2
    Re: Abstract class

    Mr.SpOOn wrote:
    Hi,
    I'm going to work on a project to represent some musical theory in
    Python, in an object oriented way.
    >
    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 "NaturalNot e", "FlatNote", "SharpNote" etc.
    >
    The idea is not original, I read it in some papers where they talk
    about an implementation in smalltalk.
    >
    I want to use Python (of course) and I'd like to know what is the
    practice in such a case. I mean, in python there aren't abstract
    classes, but I read about some way to emulate the same behavior.
    >
    What do you suggest me?
    >
    Thanks,
    Carlo
    I think the issue is one of terminology not features. Note would be a base
    class and it can have attributes (properties) and behavior (methods).
    NaturalNote, FlatNote, SharpNote would inherit from Note.

    -Larry

    Comment

    • Roy Smith

      #3
      Re: Abstract class

      In article <mailman.1008.1 221408214.3487. python-list@python.org >,
      Mr.SpOOn <mr.spoon21@gma il.comwrote:
      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 "NaturalNot e", "FlatNote", "SharpNote" etc.
      What properties or behaviors does SharpNote have which NaturalNote doesn't?
      Unless there is some new behavior, you don't need subclasses.

      Are you also going to have DoubleSharpNote and DoubleFlatNote?

      Consider the following code:

      note1 = SharpNote("E4")
      note2 = NaturalNote("F4 ")
      if note1 == note2:
      print "the same note"
      else
      print "different notes"

      what should it print?

      Comment

      • Christian Heimes

        #4
        Re: Abstract class

        Roy Smith wrote:
        >Are you also going to have DoubleSharpNote and DoubleFlatNote?
        >
        Consider the following code:
        >
        note1 = SharpNote("E4")
        note2 = NaturalNote("F4 ")
        if note1 == note2:
        print "the same note"
        else
        print "different notes"
        >
        what should it print?
        Hehe, tricky question.

        The answer depends on the music instrument and tuning. On a piano E# and
        F are on the same key but other instruments (e.g. a violin) treat E# and
        F as different notes. The enharmonic equivalent E# == F is only valid
        for some instruments and tunes - noticeable for modern tunes like equal
        temperament.

        Christian

        Comment

        • Mr.SpOOn

          #5
          Re: Abstract class

          Gary Harron:
          >I believe you are mixing up class *inheritance* and *abstract* classes.
          >Class inheritance (with Python has has for years) is how one class inherits >behavior/properties/attributes from another class. The class being inherited from is >called the base class. This is probably what you want.
          Well, I know the difference between an abstract class and an inherited
          one. The idea was to create a main class Note, with abstract methods,
          and implement these methods in the other classes.

          On Sun, Sep 14, 2008 at 7:56 PM, Roy Smith <roy@panix.comw rote:
          What properties or behaviors does SharpNote have which NaturalNote doesn't?
          Unless there is some new behavior, you don't need subclasses.
          Well, from a SharpNote I can obtain the relative NaturalNote. So if I
          have a C# I can call

          natural('C#') and get 'C'

          While in the class NaturalNote I don't need such a method, but I need
          two methods to get the sharped and flatted version
          Are you also going to have DoubleSharpNote and DoubleFlatNote?
          Yes, that's an option.
          Consider the following code:
          >
          note1 = SharpNote("E4")
          note2 = NaturalNote("F4 ")
          if note1 == note2:
          print "the same note"
          else
          print "different notes"
          >
          what should it print?
          Well, that's not so simple. The idea is that I use a notation (A, B,
          C, D...) and an integer (a distance expressed in semitones) to
          identify a note.

          Anyway, I think I need an abstract class. Or not?

          Comment

          • Gary Herron

            #6
            Re: Abstract class

            Mr.SpOOn wrote:
            Gary Harron:
            >
            >I believe you are mixing up class *inheritance* and *abstract* classes.
            >>
            >
            >
            >Class inheritance (with Python has has for years) is how one class inherits >behavior/properties/attributes from another class. The class being inherited from is >called the base class. This is probably what you want.
            >>
            >
            Well, I know the difference between an abstract class and an inherited
            one. The idea was to create a main class Note, with abstract methods,
            and implement these methods in the other classes.
            >
            On Sun, Sep 14, 2008 at 7:56 PM, Roy Smith <roy@panix.comw rote:
            >
            >What properties or behaviors does SharpNote have which NaturalNote doesn't?
            >Unless there is some new behavior, you don't need subclasses.
            >>
            >
            Well, from a SharpNote I can obtain the relative NaturalNote. So if I
            have a C# I can call
            >
            natural('C#') and get 'C'
            >
            While in the class NaturalNote I don't need such a method, but I need
            two methods to get the sharped and flatted version
            >
            >
            >Are you also going to have DoubleSharpNote and DoubleFlatNote?
            >>
            >
            Yes, that's an option.
            >
            >
            >Consider the following code:
            >>
            >note1 = SharpNote("E4")
            >note2 = NaturalNote("F4 ")
            >if note1 == note2:
            > print "the same note"
            >else
            > print "different notes"
            >>
            >what should it print?
            >>
            >
            Well, that's not so simple. The idea is that I use a notation (A, B,
            C, D...) and an integer (a distance expressed in semitones) to
            identify a note.
            >
            Anyway, I think I need an abstract class. Or not?
            >
            No! Definitely not! You need inheritance of a class from a base
            class.

            Implement your base class with whatever shared methods you want.
            Implement your derived class with methods (new, inherited or
            reimplemented) as you like.
            (If you wish to consider the base class "abstract", just agree with
            yourself to not instantiate it.)

            Please forget about Abstract Base Classes. They have nothing to do with
            what you want, and are a *HUGE* overkill for your application. They
            are not (yet) even part of standard Python, and are used primarily for a
            class implementor to guarantee to a user of a class that it provides a
            specific interface.

            Gary Herron

            Comment

            • Mr.SpOOn

              #7
              Re: Abstract class

              On Sun, Sep 14, 2008 at 9:15 PM, Gary Herron <gherron@island training.comwro te:
              Please forget about Abstract Base Classes. They have nothing to do with
              what you want, and are a *HUGE* overkill for your application. They are
              not (yet) even part of standard Python, and are used primarily for a class
              implementor to guarantee to a user of a class that it provides a specific
              interface.
              Ok :D
              I started to think to abstract classes just because in the paper it
              uses an abstract class in SmallTalk.

              Anyway, thanks.

              Comment

              • Aaron \Castironpi\ Brady

                #8
                Re: Abstract class

                On Sep 14, 2:15 pm, Gary Herron <gher...@island training.comwro te:
                Mr.SpOOn wrote:
                Gary Harron:
                >
                I believe you are mixing up class *inheritance* and *abstract* classes..
                >
                Class inheritance (with Python has has for years) is how one class inherits >behavior/properties/attributes from another class.  The class being inherited from is >called the base class.  This is probably what you want.
                >
                Well, I know the difference between an abstract class and an inherited
                one. The idea was to create a main class Note, with abstract methods,
                and implement these methods in the other classes.
                >
                On Sun, Sep 14, 2008 at 7:56 PM, Roy Smith <r...@panix.com wrote:
                >
                What properties or behaviors does SharpNote have which NaturalNote doesn't?
                Unless there is some new behavior, you don't need subclasses.
                >
                Well, from a SharpNote I can obtain the relative NaturalNote. So if I
                have a C# I can call
                >
                natural('C#')  and get 'C'
                >
                While in the class NaturalNote I don't need such a method, but I need
                two methods to get the sharped and flatted version
                >
                Are you also going to have DoubleSharpNote and DoubleFlatNote?
                >
                Yes, that's an option.
                >
                Consider the following code:
                >
                note1 = SharpNote("E4")
                note2 = NaturalNote("F4 ")
                if note1 == note2:
                  print "the same note"
                else
                  print "different notes"
                >
                what should it print?
                >
                Well, that's not so simple. The idea is that I use a notation (A, B,
                C, D...) and an integer (a distance expressed in semitones) to
                identify a note.
                >
                Anyway, I think I need an abstract class. Or not?
                >
                No!  Definitely not!    You need inheritance of a class from a base
                class.  
                Just brainstorming with you. You can support operations on the
                objects: note minus note = interval, note plus interval = note, and so
                on. (WARNING! Spoilers. Untested.)

                note1 = NaturalNote("G" )
                note2 = NaturalNote("C" )
                interval= note1- note2
                print interval
                : <MajorInterva l '5th'>

                note1 = SharpNote("G")
                note2 = NaturalNote("C" )
                interval= note1- note2
                print interval
                : <MinorInterva l '6th'>

                WholeStep= MajorInterval( 2 )
                print WholeStep
                : <MajorInterva l '2nd'>
                note1 = NaturalNote("C" )
                print note1- WholeStep
                : <FlatNote("B" )>

                However, from what I understand (brass player), there are some cases
                in which you'll want that to be:

                print note1- WholeStep
                : <SharpNote("A") >

                What do you want to determine that? What about a KeyOf class?

                key= KeyOf( NaturalNote( "F" ) )
                #'key' has special subclasses
                note1 = key.NaturalNote ("C")
                print note1- WholeStep
                : <FlatNote("B" )>

                key= KeyOf( SharpNote( "G" ) )
                note1 = key.NaturalNote ("C")
                print note1- WholeStep
                : <SharpNote("A") >

                Further, perhaps you want a 'WrittenNote' class which refers to the
                key signature it's written in:

                key= KeyOf( NaturalNote( "F" ) )
                note1 = key.WrittenNote ("B")
                print note1
                : <FlatNote("B" )>

                I do not immediately see how to represent clefs and staves: whether
                you'll need them at all, or whether they're just writing/rendering
                techniques.

                As for scales and chords, are there any special methods you want on
                them, or would a tuple of Note instances suffice?

                triad= ( NaturalNote( "C" ), NaturalNote( "E" ), NaturalNote( "G" ) )

                One special method might be:

                triad= Triad( NaturalNote( "C" ), NaturalNote( "E" ),
                NaturalNote( "G" ) )
                print triad
                : <C-Major triad>
                triad= Triad( NaturalNote( "E" ), NaturalNote( "G" ),
                NaturalNote( "C" ) )
                print triad
                : <C-Major triad 1st inversion>

                I forgot about octaves, which can complicate the constructors.

                octave= Octave( 4 ) #middle C
                triad= Triad( octave.NaturalN ote( "E" ), octave.NaturalN ote( "G" ),
                octave.up.Natur alNote( "C" ) )
                print triad
                : <C-Major triad 1st inversion>

                Or:

                octave= Octave( 4 )
                triad= Triad( NaturalNote( "E", octave ), NaturalNote( "G", octave ),
                NaturalNote( "C", octave.up ) )
                print triad
                : <C-Major triad 1st inversion>

                And abbreviate the name for the interval.

                octaveint= MajorInterval( 8 )

                Abstract scales can be a tuple of intervals, where concrete scales are
                a tuple of notes.

                majorscale= ( Tonic, WholeStep, WholeStep, HalfStep,
                WholeStep, WholeStep, WholeStep, HalfStep )

                majorCscale= [ NaturalNote( "C" )+ x for x in majorscale ]
                majorDscale= [ NaturalNote( "D" )+ x for x in majorscale ]

                To get a little more creative, you could just denote sharps and flats
                with a parameter:

                note1 = Note("G", sharp)
                #Or: note1 = Note("G", Note.sharp)
                note2 = Note("C")
                interval= note1- note2
                print interval
                : <MinorInterva l '6th'>

                It would just take some 'parameter magic' to interpret them right when
                paired with octaves:

                octave= Octave( 4 ) #middle C
                note1= Note( "C", sharp, octave )
                note2= Note( "C", octave )
                note3= Note( "C", octave, sharp )
                note4= Note( "C", sharp )

                That part just takes a little untwisting. (Untested.)

                class Note:
                defaultoctave= 4
                def __init__( self, *args ):
                octave= Octave( self.defaultoct ave )
                accidental= Natural( )
                for arg in args:
                if issubclass( arg, Accidental ):
                accidental= arg
                elif issubclass( arg, Octave ):
                octave= arg
                elif type( arg ) is str:
                name= arg

                Or use keywords to specify.

                octave= Octave( 4 ) #middle C
                note1= Note( "C", sharp, octave )
                note2= Note( "C", octave= octave )
                note3= Note( "C", octave= octave, accidental= sharp )
                note4= Note( "C", sharp )

                class Note:
                defaultoctave= 4
                def __init__( self, name, octave= None, accidental= Natural( ) ):
                if octave is None:
                octave= self.defaultoct ave

                It's open to debate whether Natural is a subclass or instance of
                Accidental.

                Comment

                • Roy Smith

                  #9
                  Re: Abstract class

                  Mr.SpOOn <mr.spoon21@gma il.comwrote:
                  Well, from a SharpNote I can obtain the relative NaturalNote. So if I
                  have a C# I can call
                  >
                  natural('C#') and get 'C'
                  And why should you not be allowed to apply natural() to a NaturalNote? I
                  would think it would just be an identity operator.
                  While in the class NaturalNote I don't need such a method, but I need
                  two methods to get the sharped and flatted version
                  Well, you may need sharp() and flat() methods on all notes. Can't you do
                  sharp('C#'), which should give you C double-sharp?
                  Consider the following code:

                  note1 = SharpNote("E4")
                  note2 = NaturalNote("F4 ")
                  if note1 == note2:
                  print "the same note"
                  else
                  print "different notes"

                  what should it print?
                  >
                  Well, that's not so simple.
                  I know it's not simple. I don't know what the correct answer is, or even
                  if the problem admits to having a single universally correct answer. I'm
                  just pointing out something you should probably think about.
                  Anyway, I think I need an abstract class. Or not?
                  Hard to tell. My advice would be to start out with a single Note class and
                  start writing code. If you get to the point where you're writing lots of
                  conditional logic in your methods (if sharp, do this, if flat, do that),
                  that's a hint that you may indeed need to refactor things into subclasses.

                  The next step would be to figure out what the right hierarchy is. Offhand,
                  I can think of three hierarchies which might be plausible:

                  ------------------------------

                  Note
                  SharpNote(Note)
                  DoubleSharpNote (Note)
                  FlatNote(Note)
                  DoubleFlatNote( Note)

                  This is a relatively flat (sorry about that) hierarchy. Note (arghhh!!)
                  that there is no explicit NaturalNote class; notes which are not sharp or
                  flat are just instances of Note.

                  ------------------------------

                  AbstractNote
                  NaturalNote(Abs tractNote)
                  SharpNote(Abstr actNote)
                  DoubleSharpNote (AbstractNote)
                  FlatNote(Abstra ctNote)
                  DoubleFlatNote( AbstractNote)

                  This one sounds like what I think you've currently got in mind.

                  ------------------------------

                  AbstractNote
                  NaturalNote(Abs tractNote)
                  SharpNote(Abstr actNote)
                  DoubleSharpNote (SharpNote)
                  FlatNote(Abstra ctNote)
                  DoubleFlatNote( FlatNote)

                  This one is similar to the above, except that the double-{sharp,flat} note
                  are subclasses of their single versions.

                  ------------------------------

                  I have no idea which is the right way to represent this. Some people would
                  agonize over the decision for days before writing a single piece of code.
                  My personal preference is to start small, get some code written, and let
                  the code teach you what makes sense. If things are flowing smoothly, you
                  probably picked a good way to organize your classes. If things are ugly
                  and you find yourself writing lots of gnarly conditional code, that's a
                  hint you're off on the wrong track. Don't be afraid to throw it away and
                  start from scratch.

                  BTW, here's another thing to think about. Is a C in a signature where C is
                  not sharp the same as a C in a signature where it is sharp but written with
                  a natural accidental? I have no clue, but when you figure out what answer
                  makes sense for you, that might help you decide how you're going to
                  organize your classes.

                  Comment

                  • Fredrik Lundh

                    #10
                    Re: Abstract class

                    Mr.SpOOn wrote:
                    Well, I know the difference between an abstract class and an inherited
                    one. The idea was to create a main class Note, with abstract methods,
                    and implement these methods in the other classes.
                    The pragmatic way to implement abstract classes in Python is to define
                    an ordinary base class, and either just leave out the methods that must
                    be defined in subclasses, or provide stub methods that simply raise a
                    NotImplementedE rror exception:

                    class AbstractThing(o bject):
                    ...
                    def some_method(sel f, args):
                    raise NotImplementedE rror

                    There are more "clever" ways to do things, but they're usually overkill.
                    Anyway, I think I need an abstract class. Or not?
                    Depends on how much generally useful functionality you can come up with
                    for the base class. It's not like Python prevents you from overriding a
                    non-abstract method, after all.

                    I suggest you skip further discussion and get to work. I'm sure you'll
                    figure out what works best for your specific case once you've written a
                    few classes.

                    </F>

                    Comment

                    • Roy Smith

                      #11
                      Re: Abstract class

                      In article
                      <64670149-9467-4ca7-9d7e-c330c1b403e4@c5 8g2000hsc.googl egroups.com>,
                      "Aaron \"Castironpi \" Brady" <castironpi@gma il.comwrote:
                      However, from what I understand (brass player),
                      If you play the trombone, you might want your Note class to allow
                      floating-point values for pitch :-)

                      Comment

                      • Aaron \Castironpi\ Brady

                        #12
                        Re: Abstract class

                        On Sep 14, 2:38 pm, Roy Smith <r...@panix.com wrote:
                        In article
                        <64670149-9467-4ca7-9d7e-c330c1b40...@c5 8g2000hsc.googl egroups.com>,
                         "Aaron \"Castironpi \" Brady" <castiro...@gma il.comwrote:
                        >
                        However, from what I understand (brass player),
                        >
                        If you play the trombone, you might want your Note class to allow
                        floating-point values for pitch :-)
                        I do! Then there are the known cases of flute players rolling the
                        mouthpiece to change pitch, trumpet players bending, violin. The 3rd
                        of a scale is slightly sharp in Bach's temper. Semi-tones come up in
                        Theory class too.

                        note= Note( "C" )
                        third= MajorInterval( 3 )
                        print note+ third
                        : <NaturalNote("E "), slightly flat>

                        Comment

                        • Gabriel Genellina

                          #13
                          Re: Abstract class

                          En Sun, 14 Sep 2008 16:15:04 -0300, Gary Herron
                          <gherron@island training.comesc ribió:
                          Please forget about Abstract Base Classes. They have nothing to do with
                          what you want, and are a *HUGE* overkill for your application. They
                          are not (yet) even part of standard Python, and are used primarily for a
                          class implementor to guarantee to a user of a class that it provides a
                          specific interface.
                          Just to say that abstract classes *are* already available in Python 2.6:

                          Python 2.6rc1+ (trunk, Sep 13 2008, 11:29:39) [MSC v.1500 32 bit (Intel)]
                          on win32
                          Type "help", "copyright" , "credits" or "license" for more information.
                          >>import abc
                          >>help(abc.abst ractmethod)
                          Help on function abstractmethod in module abc:

                          abstractmethod( funcobj)
                          A decorator indicating abstract methods.

                          Requires that the metaclass is ABCMeta or derived from it. A
                          class that has a metaclass derived from ABCMeta cannot be
                          instantiated unless all of its abstract methods are overridden.
                          The abstract methods can be called using any of the the normal
                          'super' call mechanisms.

                          Usage:

                          class C(metaclass=ABC Meta):
                          @abstractmethod
                          def my_abstract_met hod(self, ...):
                          ...

                          --
                          Gabriel Genellina

                          Comment

                          • Steven D'Aprano

                            #14
                            Re: Abstract class

                            On Sun, 14 Sep 2008 12:15:04 -0700, Gary Herron wrote:
                            (If you wish to consider the base class "abstract", just agree with
                            yourself to not instantiate it.)
                            That's certainly the most lightweight option.

                            Please forget about Abstract Base Classes. They have nothing to do with
                            what you want, and are a *HUGE* overkill for your application. They
                            are not (yet) even part of standard Python, and are used primarily for a
                            class implementor to guarantee to a user of a class that it provides a
                            specific interface.

                            You can implement a lightweight abstract base class in Python fairly
                            easily, by adding two lines to the __init__ method of your base class.


                            class Base(object):
                            def __init__(self):
                            if self.__class__ is Base:
                            raise NotImplementedE rror("Abstract base class")
                            def method(self, *args):
                            return len(args) # or something useful


                            If you want to get fancy, you can define a custom exception instead of
                            using NotImplementedE rror. And that's about all you need. You can get
                            more complicated if you want, but why bother?

                            If I may be excused a slight digression, I don't get the "abstract" part
                            of ABCs. The prohibition against instantiating them just seems very
                            artificial, I don't see what benefit it gets you. Wikipedia says:

                            "The fact that many languages disallow instantiation of abstract types
                            (and force subtypes to implement all needed functionality) further
                            ensures program correctness."



                            but I don't see how that follows (except possibly in very special cases).
                            Given that the set of instances of class B is empty, how does that help
                            you know that B.method is correct?



                            --
                            Steven

                            Comment

                            • Stephen Horne

                              #15
                              Re: Abstract class (irrelevant blethering)

                              On Sun, 14 Sep 2008 21:19:06 +0200, Mr.SpOOn <mr.spoon21@gma il.com>
                              wrote:
                              >I started to think to abstract classes just because in the paper it
                              >uses an abstract class in SmallTalk.
                              FWIW, it was obvious to me that you was using the term "abstract" in
                              the wider sense. You did say about the Smalltalk paper, after all. And
                              to me, any Python class that you treat as abstract is abstract. That
                              was always how the word "abstract" was used by Python users in the
                              past.

                              Now, it seems that there's a specific Python feature to enforce
                              abstractness. That's nice to know, and I'll have to do some reading.
                              As I said elsewhere, my Python's getting a bit rusty. But I doubt I'll
                              use it for the "overkill" reason. To me, the last important feature
                              added to Python was conditional expressions in 2.5 IIRC, and while I
                              love some features that others see as bloat, there's a lot that came
                              in earlier versions that I'm never likely to use.

                              For example, to me the term "property" is basically a trivial design
                              pattern or an object-oriented principle. I don't really see the need
                              for the language feature. I can define getter and setter methods all
                              by myself, and I don't really see the benefit of disguising them as
                              member variables. I used to like the idea, but now I'm more of an
                              occasional Python user, I can't be bothered looking up the syntax.

                              It's a strange role reversal - I used to be very enthusiatic about new
                              features back when others kept saying Python 1.5 is all you need.

                              Comment

                              Working...