Домашни > Pitches love the D > Решения > Решението на Владимир Коцев

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

8 точки общо

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

  1VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'}
  2VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
  3INTERVALS = {
  4    0: 'unison',
  5    1: 'minor 2nd',
  6    2: 'major 2nd',
  7    3: 'minor 3rd',
  8    4: 'major 3rd',
  9    5: 'perfect 4th',
 10    6: 'diminished 5th',
 11    7: 'perfect 5th',
 12    8: 'minor 6th',
 13    9: 'major 6th',
 14    10: 'minor 7th',
 15    11: 'major 7th'}
 16
 17
 18class Tone:
 19    """Represents a musical tone."""
 20
 21    def __init__(self, note):
 22        self.note = note
 23
 24    @property
 25    def note(self):
 26        return self._note
 27
 28    @note.setter
 29    def note(self, value):
 30        if value not in VALID_TONES_SET:
 31            raise ValueError(f'{value} is not a valid musical tone')
 32        self._note = value
 33
 34    def __str__(self):
 35        return self._note
 36
 37    def __eq__(self, other):
 38        if isinstance(other, Tone):
 39            return self._note == other._note
 40        return False
 41
 42    def __hash__(self):
 43        return hash(self._note)
 44
 45    def __add__(self, other):
 46        if type(other) == Tone:
 47            return Chord(self, other)
 48        elif type(other) == Interval:
 49            tone_index = VALID_TONES_LIST.index(self._note)
 50            new_index = (tone_index + other.steps) % len(VALID_TONES_LIST)
 51            return Tone(VALID_TONES_LIST[new_index])
 52        else:
 53            raise TypeError('Invalid operation')
 54
 55    def __sub__(self, other):
 56        if type(other) == Tone:
 57            first_tone_index = VALID_TONES_LIST.index(self._note)
 58            second_tone_index = VALID_TONES_LIST.index(str(other))
 59            steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST)
 60            return Interval(steps)
 61        elif type(other) == Interval:
 62            tone_index = VALID_TONES_LIST.index(self._note)
 63            new_index = (tone_index - other.steps) % len(VALID_TONES_LIST)
 64            return Tone(VALID_TONES_LIST[new_index])
 65        else:
 66            raise TypeError('Invalid operation')
 67
 68
 69class Interval:
 70    """Represents a musical interval."""
 71
 72    def __init__(self, steps):
 73        self._steps = steps
 74
 75    @property
 76    def steps(self):
 77        return self._steps
 78
 79    @steps.setter
 80    def steps(self, value):
 81        if value < 0:
 82            raise ValueError(f'{value} is not a positive number')
 83
 84        self._steps = value % 12
 85
 86    def __str__(self):
 87        return INTERVALS[self._steps]
 88
 89    def __add__(self, other):
 90        if type(other) == Interval:
 91            new_steps = (self.steps + other.steps) % 12
 92            return Interval(new_steps)
 93
 94        raise TypeError(f'Cannot add {type(other)} to Interval')
 95
 96    def __neg__(self):
 97        return Interval(-self._steps)
 98
 99
100class Chord:
101    """Represents a musical chord."""
102
103    def __init__(self, main_tone, *tones):
104        self._tones = {main_tone, *tones}
105        self._main_tone = main_tone  # Keep root so we don't cast to list
106        if len(self._tones) < 2:
107            raise TypeError('Cannot have a chord made of only 1 unique tone')
108
109    def __str__(self):
110        root_index = VALID_TONES_LIST.index(str(self._main_tone))
111        sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST))
112
113        return "-".join(str(tone) for tone in sorted_tones)
114
115    def __add__(self, other):
116        if type(other) == Tone:
117            new_tones = self._tones | {other}  # Union of sets
118            return Chord(self._main_tone, *new_tones)
119        elif type(other) == Chord:
120            new_tones = self._tones | other._tones  # Union of sets
121            new_main_tone = self._main_tone
122            return Chord(new_main_tone, *new_tones)
123        else:
124            raise TypeError('Invalid operation')
125
126    def __sub__(self, other):
127        if type(other) == Tone:
128            if other not in self._tones:
129                raise TypeError(f"Cannot remove tone {other} from chord {self}")
130
131            new_tones = self._tones - {other}
132            if len(new_tones) < 2:
133                raise TypeError("Cannot have a chord made of only 1 unique tone")
134
135            new_main_tone = next(iter(new_tones))
136            return Chord(new_main_tone, *(new_tones - {new_main_tone}))
137
138        raise TypeError(f"Cannot subtract {type(other)} from Chord")
139
140    def is_minor(self):
141        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
142        for tone in self._tones:
143            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3:
144                return True
145
146        return False
147
148    def is_major(self):
149        steps = 0
150        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
151        for tone in self._tones:
152            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4:
153                return True
154
155        return False
156
157    def is_power_chord(self):
158        if not self.is_minor() and not self.is_major():
159            return True
160
161        return False
162
163    def transposed(self, interval):
164        if type(interval) != Interval:
165            raise TypeError("Transposition requires an Interval object")
166
167        transposed_tones = set()
168        for tone in self._tones:
169            original_index = VALID_TONES_LIST.index(str(tone))
170            transposed_index = (original_index + interval.steps) % 12
171            transposed_tone = Tone(VALID_TONES_LIST[transposed_index])
172            transposed_tones.add(transposed_tone)
173
174        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
175        transposed_main_tone_index = (main_tone_index + interval.steps) % 12
176        transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index])
177
178        return Chord(transposed_main_tone, *transposed_tones)

....FFF....EE.....F........F..F......
======================================================================
ERROR: test_interval_negative (test.TestBasicIntervalFunctionality.test_interval_negative)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 51, in test_interval_negative
self.assertEqual(str(minor_2nd), "minor 2nd")
^^^^^^^^^^^^^^
File "/tmp/solution.py", line 87, in __str__
return INTERVALS[self._steps]
~~~~~~~~~^^^^^^^^^^^^^
KeyError: -11

======================================================================
ERROR: test_interval_overflow (test.TestBasicIntervalFunctionality.test_interval_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 46, in test_interval_overflow
self.assertEqual(str(major_7th), "major 7th")
^^^^^^^^^^^^^^
File "/tmp/solution.py", line 87, in __str__
return INTERVALS[self._steps]
~~~~~~~~~^^^^^^^^^^^^^
KeyError: 155

======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_is_major
self.assertTrue(a_major_chord.is_major())
AssertionError: False is not true

======================================================================
FAIL: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_is_minor
self.assertTrue(a_minor_chord.is_minor())
AssertionError: False is not true

======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_is_power_chord
self.assertFalse(a_minor_chord.is_power_chord())
AssertionError: True is not false

======================================================================
FAIL: test_add_interval_to_tone_left_side_error (test.TestOperations.test_add_interval_to_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 211, in test_add_interval_to_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "Cannot add <class 'solution.Tone'> to Interval" != 'Invalid operation'
- Cannot add <class 'solution.Tone'> to Interval
+ Invalid operation

======================================================================
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

======================================================================
FAIL: test_subtract_tone_from_chord (test.TestOperations.test_subtract_tone_from_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 277, in test_subtract_tone_from_chord
self.assertEqual(str(result_chord), "F-G#")
AssertionError: 'G#-F' != 'F-G#'
- G#-F
+ F-G#

----------------------------------------------------------------------
Ran 37 tests in 0.002s

FAILED (failures=6, errors=2)

Дискусия
Владимир Коцев
05.11.2024 13:16

Окей, благодаря!
Георги Кунчев
05.11.2024 13:12

Ние няма да тестваме с невалидни данни, така че ако грешките ти се хвърлят само в такива неочаквани случаи, не би трябвало никога да стигнем до тях.
Владимир Коцев
05.11.2024 12:43

Хвърлям допълнителни грешки спрямо условието и имайки в предвид ООП-то смятам, че трябва да бъдат покрити edge кейсовете. Ако биха били проблем за тестовете при оценяване ще ги премахна
История

f1VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'}f1VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'}
2VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']2VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
3INTERVALS = {3INTERVALS = {
4    0: 'unison',4    0: 'unison',
5    1: 'minor 2nd',5    1: 'minor 2nd',
6    2: 'major 2nd',6    2: 'major 2nd',
7    3: 'minor 3rd',7    3: 'minor 3rd',
8    4: 'major 3rd',8    4: 'major 3rd',
9    5: 'perfect 4th',9    5: 'perfect 4th',
10    6: 'diminished 5th',10    6: 'diminished 5th',
11    7: 'perfect 5th',11    7: 'perfect 5th',
12    8: 'minor 6th',12    8: 'minor 6th',
13    9: 'major 6th',13    9: 'major 6th',
14    10: 'minor 7th',14    10: 'minor 7th',
15    11: 'major 7th'}15    11: 'major 7th'}
1616
1717
18class Tone:18class Tone:
19    """Represents a musical tone."""19    """Represents a musical tone."""
2020
21    def __init__(self, note):21    def __init__(self, note):
22        self.note = note22        self.note = note
2323
24    @property24    @property
25    def note(self):25    def note(self):
26        return self._note26        return self._note
2727
28    @note.setter28    @note.setter
29    def note(self, value):29    def note(self, value):
30        if value not in VALID_TONES_SET:30        if value not in VALID_TONES_SET:
31            raise ValueError(f'{value} is not a valid musical tone')31            raise ValueError(f'{value} is not a valid musical tone')
32        self._note = value32        self._note = value
3333
34    def __str__(self):34    def __str__(self):
35        return self._note35        return self._note
3636
37    def __eq__(self, other):37    def __eq__(self, other):
38        if isinstance(other, Tone):38        if isinstance(other, Tone):
39            return self._note == other._note39            return self._note == other._note
40        return False40        return False
4141
42    def __hash__(self):42    def __hash__(self):
43        return hash(self._note)43        return hash(self._note)
4444
45    def __add__(self, other):45    def __add__(self, other):
46        if type(other) == Tone:46        if type(other) == Tone:
47            return Chord(self, other)47            return Chord(self, other)
48        elif type(other) == Interval:48        elif type(other) == Interval:
49            tone_index = VALID_TONES_LIST.index(self._note)49            tone_index = VALID_TONES_LIST.index(self._note)
50            new_index = (tone_index + other.steps) % len(VALID_TONES_LIST)50            new_index = (tone_index + other.steps) % len(VALID_TONES_LIST)
51            return Tone(VALID_TONES_LIST[new_index])51            return Tone(VALID_TONES_LIST[new_index])
52        else:52        else:
53            raise TypeError('Invalid operation')53            raise TypeError('Invalid operation')
5454
55    def __sub__(self, other):55    def __sub__(self, other):
56        if type(other) == Tone:56        if type(other) == Tone:
57            first_tone_index = VALID_TONES_LIST.index(self._note)57            first_tone_index = VALID_TONES_LIST.index(self._note)
58            second_tone_index = VALID_TONES_LIST.index(str(other))58            second_tone_index = VALID_TONES_LIST.index(str(other))
59            steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST)59            steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST)
60            return Interval(steps)60            return Interval(steps)
61        elif type(other) == Interval:61        elif type(other) == Interval:
62            tone_index = VALID_TONES_LIST.index(self._note)62            tone_index = VALID_TONES_LIST.index(self._note)
63            new_index = (tone_index - other.steps) % len(VALID_TONES_LIST)63            new_index = (tone_index - other.steps) % len(VALID_TONES_LIST)
64            return Tone(VALID_TONES_LIST[new_index])64            return Tone(VALID_TONES_LIST[new_index])
65        else:65        else:
66            raise TypeError('Invalid operation')66            raise TypeError('Invalid operation')
6767
6868
69class Interval:69class Interval:
70    """Represents a musical interval."""70    """Represents a musical interval."""
7171
72    def __init__(self, steps):72    def __init__(self, steps):
73        self._steps = steps73        self._steps = steps
7474
75    @property75    @property
76    def steps(self):76    def steps(self):
77        return self._steps77        return self._steps
7878
79    @steps.setter79    @steps.setter
80    def steps(self, value):80    def steps(self, value):
81        if value < 0:81        if value < 0:
82            raise ValueError(f'{value} is not a positive number')82            raise ValueError(f'{value} is not a positive number')
8383
84        self._steps = value % 1284        self._steps = value % 12
8585
86    def __str__(self):86    def __str__(self):
87        return INTERVALS[self._steps]87        return INTERVALS[self._steps]
8888
89    def __add__(self, other):89    def __add__(self, other):
90        if type(other) == Interval:90        if type(other) == Interval:
91            new_steps = (self.steps + other.steps) % 1291            new_steps = (self.steps + other.steps) % 12
92            return Interval(new_steps)92            return Interval(new_steps)
9393
94        raise TypeError(f'Cannot add {type(other)} to Interval')94        raise TypeError(f'Cannot add {type(other)} to Interval')
9595
96    def __neg__(self):96    def __neg__(self):
97        return Interval(-self._steps)97        return Interval(-self._steps)
9898
9999
100class Chord:100class Chord:
101    """Represents a musical chord."""101    """Represents a musical chord."""
102102
103    def __init__(self, main_tone, *tones):103    def __init__(self, main_tone, *tones):
104        self._tones = {main_tone, *tones}104        self._tones = {main_tone, *tones}
105        self._main_tone = main_tone  # Keep root so we don't cast to list105        self._main_tone = main_tone  # Keep root so we don't cast to list
106        if len(self._tones) < 2:106        if len(self._tones) < 2:
107            raise TypeError('Cannot have a chord made of only 1 unique tone')107            raise TypeError('Cannot have a chord made of only 1 unique tone')
108108
109    def __str__(self):109    def __str__(self):
110        root_index = VALID_TONES_LIST.index(str(self._main_tone))110        root_index = VALID_TONES_LIST.index(str(self._main_tone))
111        sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST))111        sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST))
112112
113        return "-".join(str(tone) for tone in sorted_tones)113        return "-".join(str(tone) for tone in sorted_tones)
114114
115    def __add__(self, other):115    def __add__(self, other):
116        if type(other) == Tone:116        if type(other) == Tone:
117            new_tones = self._tones | {other}  # Union of sets117            new_tones = self._tones | {other}  # Union of sets
118            return Chord(self._main_tone, *new_tones)118            return Chord(self._main_tone, *new_tones)
119        elif type(other) == Chord:119        elif type(other) == Chord:
120            new_tones = self._tones | other._tones  # Union of sets120            new_tones = self._tones | other._tones  # Union of sets
121            new_main_tone = self._main_tone121            new_main_tone = self._main_tone
122            return Chord(new_main_tone, *new_tones)122            return Chord(new_main_tone, *new_tones)
123        else:123        else:
124            raise TypeError('Invalid operation')124            raise TypeError('Invalid operation')
125125
126    def __sub__(self, other):126    def __sub__(self, other):
127        if type(other) == Tone:127        if type(other) == Tone:
128            if other not in self._tones:128            if other not in self._tones:
129                raise TypeError(f"Cannot remove tone {other} from chord {self}")129                raise TypeError(f"Cannot remove tone {other} from chord {self}")
130130
131            new_tones = self._tones - {other}131            new_tones = self._tones - {other}
132            if len(new_tones) < 2:132            if len(new_tones) < 2:
133                raise TypeError("Cannot have a chord made of only 1 unique tone")133                raise TypeError("Cannot have a chord made of only 1 unique tone")
134134
135            new_main_tone = next(iter(new_tones))135            new_main_tone = next(iter(new_tones))
136            return Chord(new_main_tone, *(new_tones - {new_main_tone}))136            return Chord(new_main_tone, *(new_tones - {new_main_tone}))
137137
138        raise TypeError(f"Cannot subtract {type(other)} from Chord")138        raise TypeError(f"Cannot subtract {type(other)} from Chord")
139139
140    def is_minor(self):140    def is_minor(self):
141        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))141        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
142        for tone in self._tones:142        for tone in self._tones:
143            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3:143            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3:
144                return True144                return True
145145
146        return False146        return False
147147
148    def is_major(self):148    def is_major(self):
149        steps = 0149        steps = 0
150        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))150        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
151        for tone in self._tones:151        for tone in self._tones:
152            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4:152            if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4:
153                return True153                return True
154154
155        return False155        return False
156156
157    def is_power_chord(self):157    def is_power_chord(self):
158        if not self.is_minor() and not self.is_major():158        if not self.is_minor() and not self.is_major():
159            return True159            return True
160160
161        return False161        return False
162162
163    def transposed(self, interval):163    def transposed(self, interval):
164        if type(interval) != Interval:164        if type(interval) != Interval:
165            raise TypeError("Transposition requires an Interval object")165            raise TypeError("Transposition requires an Interval object")
166166
167        transposed_tones = set()167        transposed_tones = set()
168        for tone in self._tones:168        for tone in self._tones:
169            original_index = VALID_TONES_LIST.index(str(tone))169            original_index = VALID_TONES_LIST.index(str(tone))
170            transposed_index = (original_index + interval.steps) % 12170            transposed_index = (original_index + interval.steps) % 12
171            transposed_tone = Tone(VALID_TONES_LIST[transposed_index])171            transposed_tone = Tone(VALID_TONES_LIST[transposed_index])
172            transposed_tones.add(transposed_tone)172            transposed_tones.add(transposed_tone)
173173
174        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))174        main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
175        transposed_main_tone_index = (main_tone_index + interval.steps) % 12175        transposed_main_tone_index = (main_tone_index + interval.steps) % 12
176        transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index])176        transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index])
177177
178        return Chord(transposed_main_tone, *transposed_tones)178        return Chord(transposed_main_tone, *transposed_tones)
t179 t
180c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G"))
181result_chord = c_major_chord - Tone("E")
182print(str(result_chord))
183#self.assertEqual(str(result_chord), "C-G")
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op