Домашни > Pitches love the D > Решения > Решението на Станислава Желязкова

Резултати
10 точки от тестове
0 точки от учител

10 точки общо

36 успешни теста
1 неуспешни теста
Код
Скрий всички коментари

  1class Tone:
  2    def __init__(self, note): 
  3        self.note = note
  4    
  5    def __str__(self): 
  6        return str(self.note)
  7    
  8    def __add__(self, other):
  9        if isinstance(other, Interval):
 10            new_index = (self.get_index() + other.number) % 12
 11            return Tone(self.get_note_by_index(new_index))
 12        elif isinstance(other, Tone):
 13            return Chord(self, other)  
 14        else:
 15            raise TypeError("Invalid operation")
 16    
 17    def __sub__(self, other):
 18        if isinstance(other, Interval):
 19            new_index = (self.get_index() - other.number) % 12
 20            return Tone(self.get_note_by_index(new_index))
 21        elif isinstance(other, Tone):
 22            interval_number = (self.get_index()-other.get_index()) % 12
 23            return Interval(interval_number)
 24        else:
 25            raise TypeError("Invalid operation")
 26
 27    def get_index(self):
 28        chromatic_scale = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
 29        return chromatic_scale.index(self.note)
 30
 31    @staticmethod
 32    def get_note_by_index(index):
 33        chromatic_scale = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
 34        return chromatic_scale[index]
 35
 36class Interval:
 37    interval_names = {
 38        0: "unison",
 39        1: "minor 2nd",
 40        2: "major 2nd",
 41        3: "minor 3rd",
 42        4: "major 3rd",
 43        5: "perfect 4th",
 44        6: "diminished 5th",
 45        7: "perfect 5th",
 46        8: "minor 6th",
 47        9: "major 6th",
 48        10: "minor 7th",
 49        11: "major 7th",
 50    }
 51
 52    def __init__(self, number): 
 53        self.number = number
 54    
 55    def __str__(self): 
 56        return self.interval_names[self.number % 12]
 57    
 58    def __neg__(self):
 59        return Interval(-self.number % 12)
 60
 61    def __add__(self, other):
 62        if isinstance(other, Interval):
 63            return Interval((self.number + other.number) % 12)
 64        else:
 65            raise TypeError("Invalid operation")
 66
 67
 68class Chord:
 69    def __init__(self, root: Tone, *tones: Tone):
 70        self.root = root
 71        self.tones = {root.note}
 72        for tone in tones:
 73            self.tones.add(tone.note)
 74
 75        if len(self.tones) <= 1:
 76            raise TypeError("Cannot have a chord made of only 1 unique tone")
 77
 78    def __str__(self): 
 79        sorted_tones = sorted(self.tones, key=lambda x: self.calculate_interval(self.root.note, x))
 80        return '-'.join(sorted_tones)
 81
 82    def is_minor(self):
 83        minor_third = Interval(3)
 84        for tone in self.tones:
 85            if self.calculate_interval(self.root.note, tone) == minor_third.number:
 86                return True
 87        return False
 88
 89    def is_major(self):
 90        major_third = Interval(4)
 91        for tone in self.tones:
 92            if self.calculate_interval(self.root.note, tone) == major_third.number:
 93                return True
 94        return False
 95
 96    def is_power_chord(self):
 97        minor_third = Interval(3)
 98        major_third = Interval(4)
 99        for tone in self.tones:
100            if (self.calculate_interval(self.root.note, tone) == minor_third.number or
101                    self.calculate_interval(self.root.note, tone) == major_third.number):
102                return False
103        return True
104
105    def calculate_interval(self, note1, note2):
106        chromatic_scale = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
107        index1 = chromatic_scale.index(note1)
108        index2 = chromatic_scale.index(note2)
109        return (index2 - index1) % 12
110
111    def __add__(self, other):
112        if isinstance(other, Tone):
113            return Chord(self.root, *[Tone(t) for t in self.tones], other)
114        elif isinstance(other, Chord):
115            return Chord(self.root, *(Tone(t) for t in (self.tones | other.tones)))
116        else:
117            raise TypeError("Invalid operation")
118
119    def __sub__(self, other):
120        if isinstance(other, Tone):
121            if other.note not in self.tones:
122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
123            new_tones = {Tone(t) for t in self.tones if t != other.note}
124            if len(new_tones) <= 1:
125                raise TypeError("Cannot have a chord made of only 1 unique tone")
126            return Chord(self.root, *new_tones)
127        else:
128            raise TypeError("Invalid operation")
129
130    def transposed(self, interval):
131        transposed_tones = []
132        for tone in self.tones:
133            original_tone = Tone(tone)
134            new_tone = original_tone + interval
135            transposed_tones.append(new_tone)
136        new_root = self.root + interval
137        return Chord(new_root, *transposed_tones)
138    

...........................F.........
======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation

----------------------------------------------------------------------
Ran 37 tests in 0.001s

FAILED (failures=1)

Дискусия
Виктор Бечев
06.11.2024 14:21

Отвъд дребните забележки - кодът е чист и четим. Едно концептуално вмятане - изборът ти да пазиш в `Chord` стринговите репрезентации на тоновете, вместо обектите `Tone`, работи в този случай, но това е защото работим с изключително прост модел. Ако се нуждаеш от много по-големи парчета функционалност, които се съдържат в класа `Tone` или самите инстанции имат state и някаква форма на идентитет, която е важна - ще е хубаво да пазиш обектите, а не стринговете _(заради което всъщност ти се налага да ги създаваш наново при някои от операциите)_. Не те съветвам да рискуваш да го оправяш с няколко часа до крайния срок, но просто помисли по темата.
История
Това решение има само една версия.