Version 2 of Music note class

#!/usr/bin/env python3

from string import ascii_uppercase
from string import ascii_lowercase

class Notes:
    octave_number = 7
    low_notes = ascii_uppercase[:octave_number]
    high_notes = ascii_lowercase[:octave_number]
    notes = low_notes + high_notes
    notes_number = range(octave_number * 2)
    notes_dict = dict(zip(notes, notes_number))
    numbers_dict = dict(zip(notes_number, notes))

    def __init__(self, _note):
        if not _note in Notes.notes:
            raise Exception("Not a valid note")
        self.note = _note
        self.note_number = Notes.notes_dict[self.note]

    def add(self, n):
        if (self.note in Notes.high_notes) and (n > 0):
            raise Exception("You can only add to low notes (ABCDEFG)")
        if (self.note in Notes.low_notes) and (n < 0):
            raise Exception("You can only subtract from notes (abcdefg)")
        if(abs(n) > Notes.octave_number):
            raise Exception("Maximum amount of increase or decrease is %d" % Notes.octave_number)

        new_number = self.note_number + n
        return Notes(Notes.numbers_dict[new_number])


x = Notes("A")
x = Notes("b")


  • Removed minus method
  • Expanded notes to two octaves
  • add now returns a Notes object
  • \$\begingroup\$ What is the question? \$\endgroup\$
    – sebix
    Commented Dec 6, 2014 at 15:13

1 Answer 1


You should be aware of Python's "magic methods" to implement class behaviours, including numerical methods like __add__ and __sub__. This allows you to write e.g. note3 = note1 + note2, rather than note3 = note1.add(note2). There are also methods for display, including __repr__, so you can easily view the instance. Additionally:

  • the class name should be singular, as this only holds one note, and constants should be UPPERCASE;
  • it's easier to handle either uppercase or lowercase and convert all input accordingly;
  • you can access class attributes via self too, which makes inheritance easier;
  • you check note in __init__, but there's nothing to protect it afterwards; and
  • number could be calculated based on note as required, rather than stored as duplicate information.

Here's an alternative implementation with those ideas in.

#!/usr/bin/env python3

from string import ascii_uppercase

class Note:

    NOTE_COUNT = 7
    NUMBERS = dict(enumerate(ascii_uppercase[:NOTE_COUNT]))
    NOTES = {v: k for k, v in NUMBERS.items()}

    def __init__(self, note=None, number=None):
        if note is not None and note.upper() in self.NOTES:
            note = note.upper()
        elif number is not None and number in self.NUMBERS:
            note = self.NUMBERS[number]
            raise Exception("Not a valid note")
        self._note = note 

    def __repr__(self):
        return "Note({._note!r})".format(self)

    def number(self):
        return self.NOTES[self._note]

    def note(self):
        return self._note

    def note(self, new_note):
        new_note = new_note.upper()
        if new_note not in self.NOTES:
            raise Exception("Not a valid note")
        self._note = new_note

    def __add__(self, other):
            num = other.number
        except AttributeError:
            num = other
        return Note(number=((self.number+num)%self.NOTE_COUNT))

    def __sub__(self, other):
            num = other.number
        except AttributeError:
            num = other
        return Note(number=((self.number-num)%self.NOTE_COUNT))

In use:

>>> B = Note("B")
>>> C = Note("C")
>>> B + C
Note('D') # not sure this addition makes sense, but it works!
>>> B.note = "X"
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    B.note = "X"
  File "<pyshell#34>", line 30, in note
    raise Exception("Not a valid note")
Exception: Not a valid note
>>> B.number

Now all you need is octaves and accidentals! You should also add some docstrings explaining what the class does and how to use it.

  • \$\begingroup\$ If you overload binary operators, you should return NotImplemented in ambiguous cases. See this discussion. Consider also the different behaviour of x = []; x = x + () and x = []; x += (). In this vein I strongly disagree with overloading __add__ to support addition of notes with numbers, but would be fine with __iadd__ doing so. \$\endgroup\$
    – Veedrac
    Commented Dec 8, 2014 at 14:24
  • 1
    \$\begingroup\$ @Veedrac if anything, I think that in this case adding Note instances makes less sense than adding a number to a Note! Interesting point though, I will take it on board. Do you have a more canonical source than something you also asserted elsewhere? \$\endgroup\$
    – jonrsharpe
    Commented Dec 8, 2014 at 14:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.