Домашни > Pitches love the D > Решения > Решението на Тихомир Галов

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

10 точки общо

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

  1TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')
  2INTERVAL_NAMES = ('unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th')
  3tone_to_index = {tone: index for index, tone in enumerate(TONES)}
  4index_to_tone = {index: tone for index, tone in enumerate(TONES)}
  5
  6
  7class Tone:
  8    def __init__(self, name):
  9        if name not in tone_to_index:
 10            raise ValueError(f"Invalid tone name: {name}")
 11        self.name = name
 12        self.index = tone_to_index[name]
 13
 14    def __str__(self):
 15        return self.name
 16
 17    def __eq__(self, other):
 18        return isinstance(other, Tone) and self.index == other.index
 19
 20    def __hash__(self): # We need it for sorting
 21        return hash(self.index)
 22
 23    def __add__(self, other):
 24        if isinstance(other, Tone):
 25            return Chord(self, other)
 26        elif isinstance(other, Interval):
 27            new_index = (self.index + other.semitones) % 12
 28            new_name = index_to_tone[new_index]
 29            return Tone(new_name)
 30        else:
 31            raise TypeError("Invalid operation")
 32
 33    def __sub__(self, other):
 34        if isinstance(other, Tone):
 35            semitones = (self.index - other.index) % 12
 36            return Interval(semitones)
 37        elif isinstance(other, Interval):
 38            new_index = (self.index - other.semitones) % 12
 39            new_name = index_to_tone[new_index]
 40            return Tone(new_name)
 41        else:
 42            raise TypeError("Invalid operation")
 43
 44
 45class Interval:
 46    def __init__(self, semitones):
 47        self.semitones = semitones
 48
 49    def __str__(self):
 50        semitones_mod = self.semitones % 12
 51        name = INTERVAL_NAMES[semitones_mod]
 52        return name
 53
 54    def __add__(self, other):
 55        if isinstance(other, Interval):
 56            total_semitones = self.semitones + other.semitones
 57            return Interval(total_semitones)
 58        else:
 59            raise TypeError("Invalid operation")
 60
 61    def __neg__(self):
 62        # need it for a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4)) case
 63        return Interval(-self.semitones)
 64
 65
 66class Chord:
 67    def __init__(self, root, *tones):
 68        if not isinstance(root, Tone):
 69            raise TypeError("Root must be a Tone")
 70        self.root = root
 71        all_tones = {root}
 72        for tone in tones:
 73            if not isinstance(tone, Tone):
 74                raise TypeError("All tones must be Tone instances")
 75            all_tones.add(tone)
 76        if len(all_tones) < 2:
 77            raise TypeError("Cannot have a chord made of only 1 unique tone")
 78        self.tones = all_tones
 79
 80    def sort_tone(self, tone):
 81        if tone.index >= self.root.index:
 82            return tone.index - self.root.index
 83
 84        return tone.index + (12 - self.root.index)
 85
 86    def __str__(self):
 87        tones_copy = self.tones.copy()
 88        tones_copy.remove(self.root)
 89        sorted_tones = sorted(tones_copy, key=self.sort_tone)
 90
 91        sorted_tones = [self.root] + list(sorted_tones)
 92        return '-'.join(str(tone) for tone in sorted_tones)
 93
 94    def is_minor(self):
 95        for tone in self.tones:
 96            if tone == self.root:
 97                continue
 98            interval = (tone.index - self.root.index) % 12
 99            if interval == 3:
100                return True
101        return False
102
103    def is_major(self):
104        for tone in self.tones:
105            if tone == self.root:
106                continue
107            interval = (tone.index - self.root.index) % 12
108            if interval == 4:
109                return True
110        return False
111
112    def is_power_chord(self):
113        return not self.is_minor() and not self.is_major()
114
115    def __add__(self, other):
116        if isinstance(other, Tone):
117            new_tones = self.tones.copy()
118            new_tones.add(other)
119            return Chord(self.root, *(new_tones - {self.root}))
120        elif isinstance(other, Chord):
121            new_tones = self.tones.union(other.tones)
122            return Chord(self.root, *(new_tones - {self.root}))
123        else:
124            raise TypeError("Invalid operation")
125
126    def __sub__(self, other):
127        if not isinstance(other, Tone):
128            raise TypeError("Invalid operation")
129        if other not in self.tones:
130            raise TypeError(f"Cannot remove tone {other} from chord {self}")
131        new_tones = self.tones.copy()
132        new_tones.remove(other)
133        if len(new_tones) < 2:
134            raise TypeError("Cannot have a chord made of only 1 unique tone")
135        new_root = self.root
136        if other == self.root:
137            new_root = next(iter(new_tones))
138            new_tones.remove(new_root)
139
140        return Chord(new_root, *(new_tones - {self.root}))
141
142    def transposed(self, interval):
143        if not isinstance(interval, Interval):
144            raise TypeError("Interval must be an Interval instance")
145        semitones = interval.semitones
146        new_tones = set()
147        for tone in self.tones:
148            new_index = (tone.index + semitones) % 12
149            new_tone = Tone(index_to_tone[new_index])
150            new_tones.add(new_tone)
151        new_root_index = (self.root.index + semitones) % 12
152        new_root = Tone(index_to_tone[new_root_index])
153        return Chord(new_root, *(new_tones - {new_root}))

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

Дискусия
История

n1tones_list = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')n1TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')
2INTERVAL_NAMES = ('unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th')
2tone_to_index = {tone: index for index, tone in enumerate(tones_list)}3tone_to_index = {tone: index for index, tone in enumerate(TONES)}
3index_to_tone = {index: tone for index, tone in enumerate(tones_list)}4index_to_tone = {index: tone for index, tone in enumerate(TONES)}
4 
5interval_names = {
6    0: 'unison',
7    1: 'minor 2nd',
8    2: 'major 2nd',
9    3: 'minor 3rd',
10    4: 'major 3rd',
11    5: 'perfect 4th',
12    6: 'diminished 5th',
13    7: 'perfect 5th',
14    8: 'minor 6th',
15    9: 'major 6th',
16    10: 'minor 7th',
17    11: 'major 7th'
18}
195
206
21class Tone:7class Tone:
22    def __init__(self, name):8    def __init__(self, name):
23        if name not in tone_to_index:9        if name not in tone_to_index:
24            raise ValueError(f"Invalid tone name: {name}")10            raise ValueError(f"Invalid tone name: {name}")
25        self.name = name11        self.name = name
26        self.index = tone_to_index[name]12        self.index = tone_to_index[name]
2713
28    def __str__(self):14    def __str__(self):
29        return self.name15        return self.name
3016
31    def __eq__(self, other):17    def __eq__(self, other):
32        return isinstance(other, Tone) and self.index == other.index18        return isinstance(other, Tone) and self.index == other.index
3319
34    def __hash__(self): # We need it for sorting20    def __hash__(self): # We need it for sorting
35        return hash(self.index)21        return hash(self.index)
3622
37    def __add__(self, other):23    def __add__(self, other):
38        if isinstance(other, Tone):24        if isinstance(other, Tone):
39            return Chord(self, other)25            return Chord(self, other)
40        elif isinstance(other, Interval):26        elif isinstance(other, Interval):
41            new_index = (self.index + other.semitones) % 1227            new_index = (self.index + other.semitones) % 12
42            new_name = index_to_tone[new_index]28            new_name = index_to_tone[new_index]
43            return Tone(new_name)29            return Tone(new_name)
44        else:30        else:
45            raise TypeError("Invalid operation")31            raise TypeError("Invalid operation")
4632
47    def __sub__(self, other):33    def __sub__(self, other):
48        if isinstance(other, Tone):34        if isinstance(other, Tone):
49            semitones = (self.index - other.index) % 1235            semitones = (self.index - other.index) % 12
50            return Interval(semitones)36            return Interval(semitones)
51        elif isinstance(other, Interval):37        elif isinstance(other, Interval):
52            new_index = (self.index - other.semitones) % 1238            new_index = (self.index - other.semitones) % 12
53            new_name = index_to_tone[new_index]39            new_name = index_to_tone[new_index]
54            return Tone(new_name)40            return Tone(new_name)
55        else:41        else:
56            raise TypeError("Invalid operation")42            raise TypeError("Invalid operation")
5743
5844
59class Interval:45class Interval:
60    def __init__(self, semitones):46    def __init__(self, semitones):
61        self.semitones = semitones47        self.semitones = semitones
6248
63    def __str__(self):49    def __str__(self):
64        semitones_mod = self.semitones % 1250        semitones_mod = self.semitones % 12
n65        name = interval_names[semitones_mod]n51        name = INTERVAL_NAMES[semitones_mod]
66        return name52        return name
6753
68    def __add__(self, other):54    def __add__(self, other):
69        if isinstance(other, Interval):55        if isinstance(other, Interval):
70            total_semitones = self.semitones + other.semitones56            total_semitones = self.semitones + other.semitones
71            return Interval(total_semitones)57            return Interval(total_semitones)
72        else:58        else:
73            raise TypeError("Invalid operation")59            raise TypeError("Invalid operation")
7460
75    def __neg__(self):61    def __neg__(self):
76        # need it for a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4)) case62        # need it for a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4)) case
77        return Interval(-self.semitones)63        return Interval(-self.semitones)
7864
7965
80class Chord:66class Chord:
81    def __init__(self, root, *tones):67    def __init__(self, root, *tones):
82        if not isinstance(root, Tone):68        if not isinstance(root, Tone):
83            raise TypeError("Root must be a Tone")69            raise TypeError("Root must be a Tone")
84        self.root = root70        self.root = root
85        all_tones = {root}71        all_tones = {root}
86        for tone in tones:72        for tone in tones:
87            if not isinstance(tone, Tone):73            if not isinstance(tone, Tone):
88                raise TypeError("All tones must be Tone instances")74                raise TypeError("All tones must be Tone instances")
89            all_tones.add(tone)75            all_tones.add(tone)
90        if len(all_tones) < 2:76        if len(all_tones) < 2:
91            raise TypeError("Cannot have a chord made of only 1 unique tone")77            raise TypeError("Cannot have a chord made of only 1 unique tone")
92        self.tones = all_tones78        self.tones = all_tones
9379
94    def sort_tone(self, tone):80    def sort_tone(self, tone):
nn81        if tone.index >= self.root.index:
95        return (tone.index - self.root.index) % 1282            return tone.index - self.root.index
83 
84        return tone.index + (12 - self.root.index)
9685
97    def __str__(self):86    def __str__(self):
nn87        tones_copy = self.tones.copy()
88        tones_copy.remove(self.root)
98        sorted_tones = sorted(self.tones, key=self.sort_tone)89        sorted_tones = sorted(tones_copy, key=self.sort_tone)
90 
91        sorted_tones = [self.root] + list(sorted_tones)
99        return '-'.join(str(tone) for tone in sorted_tones)92        return '-'.join(str(tone) for tone in sorted_tones)
10093
101    def is_minor(self):94    def is_minor(self):
102        for tone in self.tones:95        for tone in self.tones:
103            if tone == self.root:96            if tone == self.root:
104                continue97                continue
105            interval = (tone.index - self.root.index) % 1298            interval = (tone.index - self.root.index) % 12
106            if interval == 3:99            if interval == 3:
107                return True100                return True
108        return False101        return False
109102
110    def is_major(self):103    def is_major(self):
111        for tone in self.tones:104        for tone in self.tones:
112            if tone == self.root:105            if tone == self.root:
113                continue106                continue
114            interval = (tone.index - self.root.index) % 12107            interval = (tone.index - self.root.index) % 12
115            if interval == 4:108            if interval == 4:
116                return True109                return True
117        return False110        return False
118111
119    def is_power_chord(self):112    def is_power_chord(self):
120        return not self.is_minor() and not self.is_major()113        return not self.is_minor() and not self.is_major()
121114
122    def __add__(self, other):115    def __add__(self, other):
123        if isinstance(other, Tone):116        if isinstance(other, Tone):
124            new_tones = self.tones.copy()117            new_tones = self.tones.copy()
125            new_tones.add(other)118            new_tones.add(other)
126            return Chord(self.root, *(new_tones - {self.root}))119            return Chord(self.root, *(new_tones - {self.root}))
127        elif isinstance(other, Chord):120        elif isinstance(other, Chord):
128            new_tones = self.tones.union(other.tones)121            new_tones = self.tones.union(other.tones)
129            return Chord(self.root, *(new_tones - {self.root}))122            return Chord(self.root, *(new_tones - {self.root}))
130        else:123        else:
131            raise TypeError("Invalid operation")124            raise TypeError("Invalid operation")
132125
133    def __sub__(self, other):126    def __sub__(self, other):
134        if not isinstance(other, Tone):127        if not isinstance(other, Tone):
135            raise TypeError("Invalid operation")128            raise TypeError("Invalid operation")
136        if other not in self.tones:129        if other not in self.tones:
137            raise TypeError(f"Cannot remove tone {other} from chord {self}")130            raise TypeError(f"Cannot remove tone {other} from chord {self}")
138        new_tones = self.tones.copy()131        new_tones = self.tones.copy()
139        new_tones.remove(other)132        new_tones.remove(other)
140        if len(new_tones) < 2:133        if len(new_tones) < 2:
141            raise TypeError("Cannot have a chord made of only 1 unique tone")134            raise TypeError("Cannot have a chord made of only 1 unique tone")
tt135        new_root = self.root
136        if other == self.root:
137            new_root = next(iter(new_tones))
138            new_tones.remove(new_root)
139 
142        return Chord(self.root, *(new_tones - {self.root}))140        return Chord(new_root, *(new_tones - {self.root}))
143141
144    def transposed(self, interval):142    def transposed(self, interval):
145        if not isinstance(interval, Interval):143        if not isinstance(interval, Interval):
146            raise TypeError("Interval must be an Interval instance")144            raise TypeError("Interval must be an Interval instance")
147        semitones = interval.semitones145        semitones = interval.semitones
148        new_tones = set()146        new_tones = set()
149        for tone in self.tones:147        for tone in self.tones:
150            new_index = (tone.index + semitones) % 12148            new_index = (tone.index + semitones) % 12
151            new_tone = Tone(index_to_tone[new_index])149            new_tone = Tone(index_to_tone[new_index])
152            new_tones.add(new_tone)150            new_tones.add(new_tone)
153        new_root_index = (self.root.index + semitones) % 12151        new_root_index = (self.root.index + semitones) % 12
154        new_root = Tone(index_to_tone[new_root_index])152        new_root = Tone(index_to_tone[new_root_index])
155        return Chord(new_root, *(new_tones - {new_root}))153        return Chord(new_root, *(new_tones - {new_root}))
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op