# Music note class - v2

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]

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


Test:

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


Changes:

• Removed minus method
• Expanded notes to two octaves
• add now returns a Notes object
• What is the question? Dec 6 '14 at 15:13

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]
else:
raise Exception("Not a valid note")
self._note = note

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

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

@property
def note(self):
return self._note

@note.setter
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

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

def __sub__(self, other):
try:
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
1


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

• 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. Dec 8 '14 at 14:24
• @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? Dec 8 '14 at 14:29