1class Tone:
2 """A class that represents a musical tone."""
3 TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
4
5 def __init__(self, tone):
6 if tone not in Tone.TONES or not tone:
7 raise ValueError("Invalid input for tone")
8 self.tone = tone
9
10 def __str__(self):
11 return self.tone
12
13 def __eq__(self, other):
14 if isinstance(other, Tone):
15 return self.tone == other.tone
16 return False
17
18 def __add__(self, other):
19 if isinstance(other, Interval):
20 return self.add_interval(other)
21 if isinstance(other, Tone):
22 return Chord(self, other)
23 raise TypeError("Invalid operation")
24
25 def __sub__(self, other):
26 if isinstance(other, Interval):
27 return self.sub_interval(other)
28 if isinstance(self, Tone) and isinstance(other, Tone):
29 return Interval(abs(self.TONES.index(self.tone) - other.TONES.index(other.tone)))
30 raise TypeError("Invalid operation")
31
32 def add_interval(self, other):
33 """A method that adds an interval to a tone and returns new tone."""
34 if isinstance(other, Tone):
35 raise TypeError("Invalid operation")
36 elif isinstance(self, Tone) and isinstance(other, Interval):
37 cur_tone_index = self.TONES.index(self.tone)
38 new_index = (cur_tone_index + other.number_of_semitones) % len(self.TONES)
39 return Tone(self.TONES[new_index])
40 raise TypeError("Invalid operation")
41
42 def sub_interval(self, other):
43 """A method that subtracts an interval to a tone and returns new tone."""
44 if isinstance(self, Tone) and isinstance(other, Interval):
45 cur_tone_index = self.TONES.index(self.tone)
46 new_index = abs((cur_tone_index - other.number_of_semitones) % len(self.TONES))
47 return Tone(self.TONES[new_index])
48 if isinstance(other, Tone):
49 raise TypeError("Invalid operation")
50 raise TypeError("Invalid operation")
51
52
53class Interval:
54 """A class that represents a musical interval."""
55 INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
56 "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
57 INTERVALS_LENGTH = len(INTERVALS)
58
59 def __init__(self, number_of_semitones):
60 if not isinstance(number_of_semitones, int):
61 raise TypeError("Invalid input for number of semitones")
62 self.number_of_semitones = number_of_semitones % Interval.INTERVALS_LENGTH
63
64 def __str__(self):
65 return self.INTERVALS[self.number_of_semitones % Interval.INTERVALS_LENGTH]
66
67 def __add__(self, other):
68 if isinstance(self, Interval) and isinstance(other, Interval):
69 return Interval(self.number_of_semitones + other.number_of_semitones)
70 raise TypeError("Invalid operation")
71
72 def __sub__(self, other):
73 raise TypeError("Invalid operation")
74
75 def __neg__(self):
76 return Interval(-self.number_of_semitones)
77
78
79class Chord:
80 """A class that represents a musical chord."""
81
82 def __init__(self, *args):
83 if len(args) == 0:
84 raise TypeError("Empty chord")
85 self.root = args[0].tone
86 self.all_tones = []
87 for cur_tone in args:
88 if cur_tone not in self.all_tones:
89 self.all_tones.append(cur_tone)
90
91 if len(self.all_tones) == 1:
92 raise TypeError("Cannot have a chord made of only 1 unique tone")
93
94 root_index = Tone.TONES.index(self.root)
95 sorted_all_tones = []
96 sorted_all_tones.append(Tone.TONES[root_index])
97
98 for i in range(1, len(Tone.TONES)):
99 current_index = (root_index + i) % len(Tone.TONES)
100 current_tone = Tone.TONES[current_index]
101 if Tone(current_tone) in self.all_tones:
102 sorted_all_tones.append(current_tone)
103 self.all_tones = sorted_all_tones
104
105 def __str__(self):
106 return "-".join(str(cur_tone) for cur_tone in self.all_tones)
107
108 def is_minor(self):
109 """A method that checks if there is a tone in the chord that together
110 with the root tone form minor 3rd interval. Returns a boolean."""
111 root_index = Tone.TONES.index(self.root)
112 for tone in self.all_tones:
113 tone_index = Tone.TONES.index(tone)
114 if tone == self.root:
115 continue
116 elif tone_index - root_index == 3:
117 return True
118 elif (root_index - tone_index) % 12 == 9:
119 return True
120 return False
121
122 def is_major(self):
123 """A method that checks if there is a tone in the chord that together
124 with the root tone form major 3rd interval. Returns a boolean."""
125 root_index = Tone.TONES.index(self.root)
126 for tone in self.all_tones:
127 tone_index = Tone.TONES.index(tone)
128 if tone == self.root:
129 continue
130 elif tone_index - root_index == 4:
131 return True
132 elif (root_index - tone_index) % 12 == 9:
133 return True
134 return False
135
136 def is_power_chord(self):
137 """A method that checks if a chord does not contain a tone that, together
138 with the root, forms a minor 3rd or a major 3rd. Returns a boolean."""
139 if not self.is_major() and not self.is_minor():
140 return True
141 return False
142
143 def __add__(self, other):
144 if isinstance(other, Tone):
145 return Chord(*map(Tone, self.all_tones + [other.tone]))
146 if isinstance(other, Chord):
147 result = []
148 for tone in self.all_tones:
149 result.append(Tone(tone))
150 for tone in other.all_tones:
151 result.append(Tone(tone))
152 return Chord(*result)
153 raise TypeError("Invalid operation")
154
155 def __sub__(self, other):
156 if isinstance(other, Tone):
157 result = []
158 additional_tone = Tone(other.tone)
159 result.append(Tone(other.tone))
160 for tone in self.all_tones:
161 if Tone(tone) == additional_tone:
162 result.remove(additional_tone)
163 else:
164 result.append(Tone(tone))
165 if additional_tone in result:
166 raise TypeError(f"Cannot remove tone {Tone(other.tone)} from chord {str(self)}")
167 return Chord(*result)
168 raise TypeError("Invalid operation")
169
170 def transposed(self, interval):
171 """A method that shifts each of its tones by the same interval."""
172 transposed_tones = [Tone(tone) + interval for tone in self.all_tones]
173 return Chord(*transposed_tones)
....F.F............................F.
======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 103, in test_is_major
self.assertFalse(a_minor_chord.is_major())
AssertionError: True is not false
======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 120, in test_is_power_chord
self.assertFalse(a_major_chord.is_power_chord())
AssertionError: True is not false
======================================================================
FAIL: test_tone_subtraction_inverse (test.TestOperations.test_tone_subtraction_inverse)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 178, in test_tone_subtraction_inverse
self.assertEqual(str(perfect_4th), "perfect 4th")
AssertionError: 'perfect 5th' != 'perfect 4th'
- perfect 5th
? ^
+ perfect 4th
? ^
----------------------------------------------------------------------
Ran 37 tests in 0.002s
FAILED (failures=3)
Росица Илиева
04.11.2024 16:33Много благодаря!
|
Георги Кунчев
04.11.2024 16:24Ето пример:
```
reference_list = ['най-малко', 'по-малко', 'нещо', 'по-голямо', 'най-голямо']
to_be_sorted = ['най-голямо', 'нещо', 'по-малко']
print(sorted(to_be_sorted, key=lambda x: reference_list.index(x))) # ['по-малко', 'нещо', 'най-голямо']
```
`key` атрибутът на `sorted` очаква функция, която връща някаква стойност. Тази стойност се използва за сортирането вместо самите стойности в `to_be_sorted`.
Създавайки функцията `lambda x: reference_list.index(x)`, просто връщаме индекса на даден елемент в списъка `reference_list`.
Ако сортираме `to_be_sorted`, използвайки не стойностите в него, а резултата от функцията, извикана със конкретния елемент, всъщност сортираме елементите сравнявайки индексите им в списъка `reference_list`.
В горния пример индексите са съответно:
4 - най-голямо (4 е резултатът от `reference_list.index('най-голямо')`;
2 - нещо (2 е резултатът от `reference_list.index('нещо')`;
1 - по-малко (1 е резултатът от `reference_list.index('по-малко')`.
Тъй като сортирането не става по стойността на текста, а по индекса (4, 2, 1), резултатът е ['по-малко', 'нещо', 'най-голямо']
|
Росица Илиева
04.11.2024 16:04Може ли да ме насочите повече как трябва да сортирам с sorted(some_list, key=lambda x: ordered_list.index(x)), тъй като ламбда функциите доста ме объркват, и както и да се опитвам да го направя по този начин, не се получава?
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | """A class that represents a musical tone.""" | 2 | """A class that represents a musical tone.""" | ||
n | 3 | tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | n | 3 | TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") |
4 | |||||
4 | def __init__(self, tone): | 5 | def __init__(self, tone): | ||
n | 5 | if tone not in Tone.tones or not tone: | n | 6 | if tone not in Tone.TONES or not tone: |
6 | raise ValueError("Invalid input for tone") | 7 | raise ValueError("Invalid input for tone") | ||
7 | self.tone = tone | 8 | self.tone = tone | ||
8 | 9 | ||||
9 | def __str__(self): | 10 | def __str__(self): | ||
n | 10 | return f"{self.tone}" | n | 11 | return self.tone |
11 | 12 | ||||
12 | def __eq__(self, other): | 13 | def __eq__(self, other): | ||
13 | if isinstance(other, Tone): | 14 | if isinstance(other, Tone): | ||
14 | return self.tone == other.tone | 15 | return self.tone == other.tone | ||
15 | return False | 16 | return False | ||
16 | 17 | ||||
17 | def __add__(self, other): | 18 | def __add__(self, other): | ||
18 | if isinstance(other, Interval): | 19 | if isinstance(other, Interval): | ||
19 | return self.add_interval(other) | 20 | return self.add_interval(other) | ||
20 | if isinstance(other, Tone): | 21 | if isinstance(other, Tone): | ||
21 | return Chord(self, other) | 22 | return Chord(self, other) | ||
n | n | 23 | raise TypeError("Invalid operation") | ||
22 | 24 | ||||
23 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
24 | if isinstance(other, Interval): | 26 | if isinstance(other, Interval): | ||
25 | return self.sub_interval(other) | 27 | return self.sub_interval(other) | ||
26 | if isinstance(self, Tone) and isinstance(other, Tone): | 28 | if isinstance(self, Tone) and isinstance(other, Tone): | ||
n | 27 | return Interval(abs(self.tones.index(self.tone) - other.tones.index(other.tone))) | n | 29 | return Interval(abs(self.TONES.index(self.tone) - other.TONES.index(other.tone))) |
30 | raise TypeError("Invalid operation") | ||||
28 | 31 | ||||
29 | def add_interval(self, other): | 32 | def add_interval(self, other): | ||
n | n | 33 | """A method that adds an interval to a tone and returns new tone.""" | ||
30 | if isinstance(other, Tone): | 34 | if isinstance(other, Tone): | ||
31 | raise TypeError("Invalid operation") | 35 | raise TypeError("Invalid operation") | ||
32 | elif isinstance(self, Tone) and isinstance(other, Interval): | 36 | elif isinstance(self, Tone) and isinstance(other, Interval): | ||
n | 33 | cur_tone_index = self.tones.index(self.tone) | n | 37 | cur_tone_index = self.TONES.index(self.tone) |
34 | new_index = (cur_tone_index + other.number_of_semitones) % len(self.tones) | 38 | new_index = (cur_tone_index + other.number_of_semitones) % len(self.TONES) | ||
35 | return Tone(self.tones[new_index]) | 39 | return Tone(self.TONES[new_index]) | ||
40 | raise TypeError("Invalid operation") | ||||
36 | 41 | ||||
37 | def sub_interval(self, other): | 42 | def sub_interval(self, other): | ||
n | n | 43 | """A method that subtracts an interval to a tone and returns new tone.""" | ||
38 | if isinstance(self, Tone) and isinstance(other, Interval): | 44 | if isinstance(self, Tone) and isinstance(other, Interval): | ||
n | 39 | cur_tone_index = self.tones.index(self.tone) | n | 45 | cur_tone_index = self.TONES.index(self.tone) |
40 | new_index = abs((cur_tone_index - other.number_of_semitones) % len(self.tones)) | 46 | new_index = abs((cur_tone_index - other.number_of_semitones) % len(self.TONES)) | ||
41 | return Tone(self.tones[new_index]) | 47 | return Tone(self.TONES[new_index]) | ||
42 | if isinstance(other, Tone): | 48 | if isinstance(other, Tone): | ||
43 | raise TypeError("Invalid operation") | 49 | raise TypeError("Invalid operation") | ||
n | n | 50 | raise TypeError("Invalid operation") | ||
51 | |||||
44 | 52 | ||||
45 | class Interval: | 53 | class Interval: | ||
46 | """A class that represents a musical interval.""" | 54 | """A class that represents a musical interval.""" | ||
n | 47 | intervals = {0 : "unison", 1 : "minor 2nd", 2 : "major 2nd", 3 : "minor 3rd", 4 : "major 3rd", 5 : "perfect 4th", | n | 55 | INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", |
48 | 6 : "diminished 5th", 7 : "perfect 5th", 8 : "minor 6th", 9 : "major 6th", 10 : "minor 7th", 11 : "major 7th"} | 56 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
57 | INTERVALS_LENGTH = len(INTERVALS) | ||||
49 | 58 | ||||
50 | def __init__(self, number_of_semitones): | 59 | def __init__(self, number_of_semitones): | ||
51 | if not isinstance(number_of_semitones, int): | 60 | if not isinstance(number_of_semitones, int): | ||
52 | raise TypeError("Invalid input for number of semitones") | 61 | raise TypeError("Invalid input for number of semitones") | ||
n | 53 | self.number_of_semitones = number_of_semitones % 12 | n | 62 | self.number_of_semitones = number_of_semitones % Interval.INTERVALS_LENGTH |
54 | 63 | ||||
55 | def __str__(self): | 64 | def __str__(self): | ||
n | 56 | return f"{self.intervals[self.number_of_semitones % 12]}" | n | 65 | return self.INTERVALS[self.number_of_semitones % Interval.INTERVALS_LENGTH] |
57 | 66 | ||||
58 | def __add__(self, other): | 67 | def __add__(self, other): | ||
59 | if isinstance(self, Interval) and isinstance(other, Interval): | 68 | if isinstance(self, Interval) and isinstance(other, Interval): | ||
60 | return Interval(self.number_of_semitones + other.number_of_semitones) | 69 | return Interval(self.number_of_semitones + other.number_of_semitones) | ||
n | 61 | n | 70 | raise TypeError("Invalid operation") | |
71 | |||||
72 | def __sub__(self, other): | ||||
73 | raise TypeError("Invalid operation") | ||||
74 | |||||
62 | def __neg__(self): | 75 | def __neg__(self): | ||
63 | return Interval(-self.number_of_semitones) | 76 | return Interval(-self.number_of_semitones) | ||
n | 64 | n | 77 | ||
78 | |||||
65 | class Chord: | 79 | class Chord: | ||
n | 66 | """A class that represents a musical chord.""" | n | 80 | """A class that represents a musical chord.""" |
81 | |||||
67 | def __init__(self, *args): | 82 | def __init__(self, *args): | ||
68 | if len(args) == 0: | 83 | if len(args) == 0: | ||
69 | raise TypeError("Empty chord") | 84 | raise TypeError("Empty chord") | ||
n | 70 | self.root = str(args[0]) | n | 85 | self.root = args[0].tone |
71 | self.all_thones = [] | 86 | self.all_tones = [] | ||
72 | for cur_tone in args: | 87 | for cur_tone in args: | ||
n | 73 | if cur_tone not in self.all_thones: | n | 88 | if cur_tone not in self.all_tones: |
74 | self.all_thones.append(cur_tone) | 89 | self.all_tones.append(cur_tone) | ||
75 | 90 | ||||
n | 76 | if len(self.all_thones) == 1: | n | 91 | if len(self.all_tones) == 1: |
77 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 92 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
78 | 93 | ||||
n | 79 | root_index = Tone.tones.index(self.root) | n | 94 | root_index = Tone.TONES.index(self.root) |
80 | sorted_all_thones = [] | 95 | sorted_all_tones = [] | ||
81 | sorted_all_thones.append(Tone.tones[root_index]) | 96 | sorted_all_tones.append(Tone.TONES[root_index]) | ||
82 | 97 | ||||
n | 83 | for i in range(1, len(Tone.tones)): | n | 98 | for i in range(1, len(Tone.TONES)): |
84 | current_index = (root_index + i) % len(Tone.tones) | 99 | current_index = (root_index + i) % len(Tone.TONES) | ||
85 | current_tone = Tone.tones[current_index] | 100 | current_tone = Tone.TONES[current_index] | ||
86 | if Tone(current_tone) in self.all_thones: | 101 | if Tone(current_tone) in self.all_tones: | ||
87 | sorted_all_thones.append(current_tone) | 102 | sorted_all_tones.append(current_tone) | ||
88 | self.all_thones = sorted_all_thones | 103 | self.all_tones = sorted_all_tones | ||
89 | 104 | ||||
90 | def __str__(self): | 105 | def __str__(self): | ||
n | 91 | return "-".join(str(cur_tone) for cur_tone in self.all_thones) | n | 106 | return "-".join(str(cur_tone) for cur_tone in self.all_tones) |
92 | 107 | ||||
93 | def is_minor(self): | 108 | def is_minor(self): | ||
n | n | 109 | """A method that checks if there is a tone in the chord that together | ||
110 | with the root tone form minor 3rd interval. Returns a boolean.""" | ||||
94 | root_index = Tone.tones.index(self.root) | 111 | root_index = Tone.TONES.index(self.root) | ||
95 | for tone in self.all_thones: | 112 | for tone in self.all_tones: | ||
96 | tone_index = Tone.tones.index(tone) | 113 | tone_index = Tone.TONES.index(tone) | ||
97 | if tone == self.root: | 114 | if tone == self.root: | ||
98 | continue | 115 | continue | ||
99 | elif tone_index - root_index == 3: | 116 | elif tone_index - root_index == 3: | ||
100 | return True | 117 | return True | ||
n | 101 | elif tone_index in (0, 1, 2) and root_index == tone_index + 9: | n | 118 | elif (root_index - tone_index) % 12 == 9: |
102 | return True | ||||
103 | elif tone_index in (9, 10, 11) and root_index == tone_index - 9: | ||||
104 | return True | 119 | return True | ||
105 | return False | 120 | return False | ||
n | 106 | n | 121 | ||
107 | def is_major(self): | 122 | def is_major(self): | ||
n | n | 123 | """A method that checks if there is a tone in the chord that together | ||
124 | with the root tone form major 3rd interval. Returns a boolean.""" | ||||
108 | root_index = Tone.tones.index(self.root) | 125 | root_index = Tone.TONES.index(self.root) | ||
109 | for tone in self.all_thones: | 126 | for tone in self.all_tones: | ||
110 | tone_index = Tone.tones.index(tone) | 127 | tone_index = Tone.TONES.index(tone) | ||
111 | if tone == self.root: | 128 | if tone == self.root: | ||
112 | continue | 129 | continue | ||
113 | elif tone_index - root_index == 4: | 130 | elif tone_index - root_index == 4: | ||
114 | return True | 131 | return True | ||
n | 115 | elif tone_index in (0, 1, 2, 3) and root_index == tone_index + 8: | n | 132 | elif (root_index - tone_index) % 12 == 9: |
116 | return True | ||||
117 | elif tone_index in (8, 9, 10, 11) and root_index == tone_index - 8: | ||||
118 | return True | 133 | return True | ||
119 | return False | 134 | return False | ||
120 | 135 | ||||
121 | def is_power_chord(self): | 136 | def is_power_chord(self): | ||
n | n | 137 | """A method that checks if a chord does not contain a tone that, together | ||
138 | with the root, forms a minor 3rd or a major 3rd. Returns a boolean.""" | ||||
122 | if self.is_major() is False and self.is_minor() is False: | 139 | if not self.is_major() and not self.is_minor(): | ||
123 | return True | 140 | return True | ||
124 | return False | 141 | return False | ||
n | 125 | n | 142 | ||
126 | def __add__(self, other): | 143 | def __add__(self, other): | ||
127 | if isinstance(other, Tone): | 144 | if isinstance(other, Tone): | ||
n | 128 | result = [] | n | 145 | return Chord(*map(Tone, self.all_tones + [other.tone])) |
129 | for tone in self.all_thones: | ||||
130 | result.append(Tone(tone)) | ||||
131 | result.append(Tone(other.tone)) | ||||
132 | return Chord(*result) | ||||
133 | if isinstance(other, Chord): | 146 | if isinstance(other, Chord): | ||
134 | result = [] | 147 | result = [] | ||
n | 135 | for tone in self.all_thones: | n | 148 | for tone in self.all_tones: |
136 | result.append(Tone(tone)) | 149 | result.append(Tone(tone)) | ||
n | 137 | for tone in other.all_thones: | n | 150 | for tone in other.all_tones: |
138 | result.append(Tone(tone)) | 151 | result.append(Tone(tone)) | ||
139 | return Chord(*result) | 152 | return Chord(*result) | ||
n | 140 | n | 153 | raise TypeError("Invalid operation") | |
154 | |||||
141 | def __sub__(self, other): | 155 | def __sub__(self, other): | ||
142 | if isinstance(other, Tone): | 156 | if isinstance(other, Tone): | ||
143 | result = [] | 157 | result = [] | ||
144 | additional_tone = Tone(other.tone) | 158 | additional_tone = Tone(other.tone) | ||
145 | result.append(Tone(other.tone)) | 159 | result.append(Tone(other.tone)) | ||
n | 146 | for tone in self.all_thones: | n | 160 | for tone in self.all_tones: |
147 | if Tone(tone) == additional_tone: | 161 | if Tone(tone) == additional_tone: | ||
148 | result.remove(additional_tone) | 162 | result.remove(additional_tone) | ||
149 | else: | 163 | else: | ||
150 | result.append(Tone(tone)) | 164 | result.append(Tone(tone)) | ||
151 | if additional_tone in result: | 165 | if additional_tone in result: | ||
n | 152 | raise TypeError(f"Cannot remove tone {Tone(other.tone)} from chord {Chord(*(Tone(tone) for tone in self.all_thones))}") | n | 166 | raise TypeError(f"Cannot remove tone {Tone(other.tone)} from chord {str(self)}") |
153 | return Chord(*result) | 167 | return Chord(*result) | ||
n | n | 168 | raise TypeError("Invalid operation") | ||
154 | 169 | ||||
155 | def transposed(self, interval): | 170 | def transposed(self, interval): | ||
t | t | 171 | """A method that shifts each of its tones by the same interval.""" | ||
156 | transposed_tones = [Tone(tone) + interval for tone in self.all_thones] | 172 | transposed_tones = [Tone(tone) + interval for tone in self.all_tones] | ||
157 | return Chord(*transposed_tones) | 173 | return Chord(*transposed_tones) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|