Домашни > Pitches love the D > Решения > Решението на Екатерина Стоянова

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

9 точки общо

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

  1class Tone:
  2    VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes
  3
  4    def __init__(self, tone):
  5        self.tone = tone
  6
  7    def __str__(self):
  8        return str(self.tone)
  9
 10    def __eq__(self, other):
 11        return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone)
 12
 13    def __hash__(self):
 14        return hash(self.tone)
 15
 16    def __and__(self, other): # using it for the major and minor functions for convenience

Имай предвид, че това дефинира поведение за бинарният оператор &. За мен лично е неинтуитивно той да изчислява разстояние между тоновете.
That being said, в домашното - не пречи, просто имай предвид, че е доста нелогично да очакваш поведение на бинарен оператор между два обекта и като резултат да получиш число (разлика). За в бъдеще.

17 return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone)) 18 19 def __add__(self, other): 20 if isinstance(other, Tone): 21 return Chord (self, other) 22 elif isinstance(other, Interval): 23 new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12 24 new_tone = self.VALID_NOTES[new_tone_index] 25 return Tone(new_tone) 26 else: 27 raise TypeError("Invalid operation") 28 29 def __sub__(self, other): 30 if isinstance(other, Tone): 31 return Interval(self&other)

Хубаво е да имаш разстояние около този оператор as well (както на други места имаш около знаците за събиране / изваждане и т.н.).

32 elif isinstance(other, Interval): 33 new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12 34 new_tone = self.VALID_NOTES[new_tone_index] 35 return Tone(new_tone) 36 else: 37 raise TypeError("Invalid operation") 38 39 40class Interval: 41 SEMITONES = {

Тук на практика имаш колекция, която достъпваш с число от 0 до броят на всички елементи минус 1. С други думи - имаш списък / кортеж.

42 0: "unison", 43 1: "minor 2nd", 44 2: "major 2nd", 45 3: "minor 3rd", 46 4: "major 3rd", 47 5: "perfect 4th", 48 6: "diminished 5th", 49 7: "perfect 5th", 50 8: "minor 6th", 51 9: "major 6th", 52 10: "minor 7th", 53 11: "major 7th" 54 } 55 56 def __init__(self, number_of_semitones): 57 self.number_of_semitones = number_of_semitones % 12 58 59 def __str__(self): 60 return self.SEMITONES[self.number_of_semitones] 61 62 def __add__(self, other): 63 if isinstance(other, Tone): 64 raise TypeError ("Invalid operation") 65 elif isinstance(other, Interval): 66 return Interval(self.number_of_semitones + other.number_of_semitones) 67 68 def __neg__(self): 69 return Interval(-self.number_of_semitones) 70 71 72class Chord: 73 CHROMATIC_SCALE = Tone.VALID_NOTES 74 75 def __init__(self, main_tone, *tones): 76 self.main_tone = main_tone 77 #remove duplicates by first using a set and then back to a list 78 unique_tones = list(set(tones) | {main_tone}) 79 if len(unique_tones) < 2: 80 raise TypeError("Cannot have a chord made of only 1 unique tone") 81 self.tones = unique_tones 82 83 def __str__(self): 84 to_return = str(self.main_tone) 85 main_tone_idx = self.CHROMATIC_SCALE.index(to_return) 86 tone_str = {str(tone) for tone in self.tones} 87 # sorting based on the main tone, NOT based on the indexation in Valid_Tones 88 for tone in Tone.VALID_NOTES[main_tone_idx + 1:]: 89 if tone in tone_str: 90 to_return += "-" + tone

Важи, но имай предвид, че избягваме конкатенация между стрингове, за предпочитане са f-strings или str.format.

91 for tone in Tone.VALID_NOTES[:main_tone_idx]: 92 if tone in tone_str: 93 to_return += "-" + tone 94 return to_return 95 96 def is_minor(self): 97 for t in self.tones: 98 if self.main_tone != t and (self.main_tone & t) == 3: # 3 is equal to the index of minor 3rd 99 return True 100 return False 101 102 def is_major(self): 103 for t in self.tones: 104 if self.main_tone != t and (self.main_tone & t) == 4: # 4 is equal to the index of major 3rd 105 return True 106 return False 107 108 def is_power_chord(self): 109 is_it_minor = self.is_minor()

Нямаш нужда от променливите, ако ги сложиш директно в return-а редът няма да стане нито прекалено сложен, нито прекалено дълъг.

110 is_it_major = self.is_major() 111 return not (is_it_major or is_it_minor) 112 113 def __add__(self, other): 114 if isinstance(other, Tone): 115 return Chord(self.main_tone, *(self.tones + [other])) 116 elif isinstance(other, Chord): 117 return Chord(self.main_tone, *(self.tones+other.tones))

И тук отново е желателно да има интервали около оператора за събиране, както 2 реда по-нагоре.

118 119 def __sub__(self, other): 120 if isinstance(other, Tone) and other in self.tones: 121 if len(self.tones) >= 3: 122 other_index = self.tones.index(other) 123 return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index])) 124 else: 125 raise TypeError("Cannot have a chord made of only 1 unique tone") 126 else: 127 raise TypeError(f"Cannot remove tone {other} from chord {self}") 128 129 def transposed(self, interval): 130 to_transpose = list() 131 to_transpose.append(self.main_tone+interval) 132 for t in self.tones:

Същото нещо можеш да направиш по-кратко с един list comprehension. Не е проблем, но определено е по-идиоматично да ползваме такъв тип инструменти, които езикът ни предоставя.

133 to_transpose.append(t + interval) 134 return Chord(*to_transpose)

....FFF....................F.......F.
======================================================================
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_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_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=5)

Дискусия
Виктор Бечев
01.11.2024 19:03

Както споменаваме често - ползвайте каквото ви е кеф в стандартната библиотека.
__hash__ е стандартен дъндър от обектният протокол, така че ползвай на воля. :)

Екатерина Стоянова
31.10.2024 17:30

Благодаря Ви! Аз иначе открих проблема - Tone не е "hashable" по подразбиране и затова не е тръгвало. Проблем ли е, че ще ползвам функцията _ hash _(), понеже я видях в GeeksForGeeks, но поне в презентациите не я намерих?

Виктор Бечев
31.10.2024 16:45

Ако правилно съм те разбрал, то проблемът ти се свежда до ниво на абстракция, което не е проблем да обсъдим.

Как да сортирам тоновете, без да дефинирам __eq__ (перифразирам твоят въпрос)?
Това, което имаш по-горе е сравнително on-point - сортирането би следвало да стане чрез атрибутът tone на обектите от тип Tone. Тоновете ти са в списък и те не са сортирани. Сортирането става чак при репрезентацията като стринг, където е и въпросната lambda функция, която често се ползва по този начин за да "зададе" алгоритъм за сортиране на sorted метода. В случая просто взима въпросния tone атрибут и прави изместването, всичко от което на теория изглежда правилно.

Ако решиш да дефинираш __eq__, ще можеш да си позволиш сортиране без тази ламбда, съответно "стандартната" употреба на sorted ще работи прекрасно, ако самото сравнение имплементираш в __eq__.

Не мога на прима виста да ти кажа защо не принтира правилните стойности, а и това вече би било равнозначно на това да дебъгваме твоя код, което вече няма да е справедливо.

Екатерина Стоянова
31.10.2024 13:44

Здравейте! Извинявам се, че Ви занимавам, но реших, че това ще бъде по-добра стратегия, отколкото да използвам ChatBot-а. Качих нарочно невалидно и (очевидно) незавършено домашно, за да Ви попитам по него. Проблемът ми идва в това как кода "уж" слага отделните тонове в лист, така че да няма повторения и с (малко помощ от чат-бота), после сортирам спрямо индекс на валидните стойности, които съм дала. Въпросът ми е - тук нужно ли е да се предефинира eq оператора, защото дори и когато го направих, пак при генерираните от бота тестове, принтеше грешно. Знам, че това може да се приеме като все едно ми помагате с домашното, така че разбирам, ако не ми отговорите, просто сама се обърках в собствения си код, а реално искам да разбера проблема 😭

История

f1class Tone:f1class Tone:
2    VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes2    VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes
33
4    def __init__(self, tone):4    def __init__(self, tone):
5        self.tone = tone5        self.tone = tone
66
7    def __str__(self):7    def __str__(self):
8        return str(self.tone)8        return str(self.tone)
99
10    def __eq__(self, other):10    def __eq__(self, other):
11        return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone)11        return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone)
1212
13    def __hash__(self):13    def __hash__(self):
14        return hash(self.tone)14        return hash(self.tone)
1515
16    def __and__(self, other): # using it for the major and minor functions for convenience16    def __and__(self, other): # using it for the major and minor functions for convenience
17        return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone))17        return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone))
1818
19    def __add__(self, other):19    def __add__(self, other):
20        if isinstance(other, Tone):20        if isinstance(other, Tone):
21            return Chord (self, other)21            return Chord (self, other)
22        elif isinstance(other, Interval):22        elif isinstance(other, Interval):
23            new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 1223            new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12
24            new_tone = self.VALID_NOTES[new_tone_index]24            new_tone = self.VALID_NOTES[new_tone_index]
25            return Tone(new_tone)25            return Tone(new_tone)
26        else:26        else:
27            raise TypeError("Invalid operation")27            raise TypeError("Invalid operation")
2828
29    def __sub__(self, other):29    def __sub__(self, other):
30        if isinstance(other, Tone):30        if isinstance(other, Tone):
31            return Interval(self&other)31            return Interval(self&other)
32        elif isinstance(other, Interval):32        elif isinstance(other, Interval):
33            new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 1233            new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12
34            new_tone = self.VALID_NOTES[new_tone_index]34            new_tone = self.VALID_NOTES[new_tone_index]
35            return Tone(new_tone)35            return Tone(new_tone)
36        else:36        else:
37            raise TypeError("Invalid operation")37            raise TypeError("Invalid operation")
3838
3939
40class Interval:40class Interval:
41    SEMITONES = {41    SEMITONES = {
42        0: "unison",42        0: "unison",
43        1: "minor 2nd",43        1: "minor 2nd",
44        2: "major 2nd",44        2: "major 2nd",
45        3: "minor 3rd",45        3: "minor 3rd",
46        4: "major 3rd",46        4: "major 3rd",
47        5: "perfect 4th",47        5: "perfect 4th",
48        6: "diminished 5th",48        6: "diminished 5th",
49        7: "perfect 5th",49        7: "perfect 5th",
50        8: "minor 6th",50        8: "minor 6th",
51        9: "major 6th",51        9: "major 6th",
52        10: "minor 7th",52        10: "minor 7th",
53        11: "major 7th"53        11: "major 7th"
54    }54    }
5555
56    def __init__(self, number_of_semitones):56    def __init__(self, number_of_semitones):
57        self.number_of_semitones = number_of_semitones % 1257        self.number_of_semitones = number_of_semitones % 12
5858
59    def __str__(self):59    def __str__(self):
60        return self.SEMITONES[self.number_of_semitones]60        return self.SEMITONES[self.number_of_semitones]
6161
62    def __add__(self, other):62    def __add__(self, other):
63        if isinstance(other, Tone):63        if isinstance(other, Tone):
64            raise TypeError ("Invalid operation")64            raise TypeError ("Invalid operation")
65        elif isinstance(other, Interval):65        elif isinstance(other, Interval):
66            return Interval(self.number_of_semitones + other.number_of_semitones)66            return Interval(self.number_of_semitones + other.number_of_semitones)
6767
68    def __neg__(self):68    def __neg__(self):
69        return Interval(-self.number_of_semitones)69        return Interval(-self.number_of_semitones)
7070
tt71 
71class Chord:72class Chord:
72    CHROMATIC_SCALE = Tone.VALID_NOTES73    CHROMATIC_SCALE = Tone.VALID_NOTES
7374
74    def __init__(self, main_tone, *tones):75    def __init__(self, main_tone, *tones):
75        self.main_tone = main_tone76        self.main_tone = main_tone
76        #remove duplicates by first using a set and then back to a list77        #remove duplicates by first using a set and then back to a list
77        unique_tones = list(set(tones) | {main_tone})78        unique_tones = list(set(tones) | {main_tone})
78        if len(unique_tones) < 2:79        if len(unique_tones) < 2:
79            raise TypeError("Cannot have a chord made of only 1 unique tone")80            raise TypeError("Cannot have a chord made of only 1 unique tone")
80        self.tones = unique_tones81        self.tones = unique_tones
8182
82    def __str__(self):83    def __str__(self):
83        to_return = str(self.main_tone)84        to_return = str(self.main_tone)
84        main_tone_idx = self.CHROMATIC_SCALE.index(to_return)85        main_tone_idx = self.CHROMATIC_SCALE.index(to_return)
85        tone_str = {str(tone) for tone in self.tones}86        tone_str = {str(tone) for tone in self.tones}
86        # sorting based on the main tone, NOT based on the indexation in Valid_Tones87        # sorting based on the main tone, NOT based on the indexation in Valid_Tones
87        for tone in Tone.VALID_NOTES[main_tone_idx + 1:]:88        for tone in Tone.VALID_NOTES[main_tone_idx + 1:]:
88            if tone in tone_str:89            if tone in tone_str:
89                to_return += "-" + tone90                to_return += "-" + tone
90        for tone in Tone.VALID_NOTES[:main_tone_idx]:91        for tone in Tone.VALID_NOTES[:main_tone_idx]:
91            if tone in tone_str:92            if tone in tone_str:
92                to_return += "-" + tone93                to_return += "-" + tone
93        return to_return94        return to_return
9495
95    def is_minor(self):96    def is_minor(self):
96        for t in self.tones:97        for t in self.tones:
97            if self.main_tone != t and (self.main_tone & t) == 3:  # 3 is equal to the index of minor 3rd98            if self.main_tone != t and (self.main_tone & t) == 3:  # 3 is equal to the index of minor 3rd
98                return True99                return True
99        return False100        return False
100101
101    def is_major(self):102    def is_major(self):
102        for t in self.tones:103        for t in self.tones:
103            if self.main_tone != t and (self.main_tone & t) == 4:  # 4 is equal to the index of major 3rd104            if self.main_tone != t and (self.main_tone & t) == 4:  # 4 is equal to the index of major 3rd
104                return True105                return True
105        return False106        return False
106107
107    def is_power_chord(self):108    def is_power_chord(self):
108        is_it_minor = self.is_minor()109        is_it_minor = self.is_minor()
109        is_it_major = self.is_major()110        is_it_major = self.is_major()
110        return not (is_it_major or is_it_minor)111        return not (is_it_major or is_it_minor)
111112
112    def __add__(self, other):113    def __add__(self, other):
113        if isinstance(other, Tone):114        if isinstance(other, Tone):
114            return Chord(self.main_tone, *(self.tones + [other]))115            return Chord(self.main_tone, *(self.tones + [other]))
115        elif isinstance(other, Chord):116        elif isinstance(other, Chord):
116            return Chord(self.main_tone, *(self.tones+other.tones))117            return Chord(self.main_tone, *(self.tones+other.tones))
117118
118    def __sub__(self, other):119    def __sub__(self, other):
119        if isinstance(other, Tone) and other in self.tones:120        if isinstance(other, Tone) and other in self.tones:
120            if len(self.tones) >= 3:121            if len(self.tones) >= 3:
121                other_index = self.tones.index(other)122                other_index = self.tones.index(other)
122                return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index]))123                return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index]))
123            else:124            else:
124                raise TypeError("Cannot have a chord made of only 1 unique tone")125                raise TypeError("Cannot have a chord made of only 1 unique tone")
125        else:126        else:
126            raise TypeError(f"Cannot remove tone {other} from chord {self}")127            raise TypeError(f"Cannot remove tone {other} from chord {self}")
127128
128    def transposed(self, interval):129    def transposed(self, interval):
129        to_transpose = list()130        to_transpose = list()
130        to_transpose.append(self.main_tone+interval)131        to_transpose.append(self.main_tone+interval)
131        for t in self.tones:132        for t in self.tones:
132            to_transpose.append(t + interval)133            to_transpose.append(t + interval)
133        return Chord(*to_transpose)134        return Chord(*to_transpose)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Tone:f1class Tone:
n2    VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] #arr to use the indexesn2    VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes
33
4    def __init__(self, tone):4    def __init__(self, tone):
n5        if tone not in self.VALID_NOTES:n
6            raise TypeError("nqkakuv text lol")
7        self.tone = tone5        self.tone = tone
86
9    def __str__(self):7    def __str__(self):
n10        return self.tonen8        return str(self.tone)
9 
10    def __eq__(self, other):
11        return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone)
12 
13    def __hash__(self):
14        return hash(self.tone)
15 
16    def __and__(self, other): # using it for the major and minor functions for convenience
17        return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone))
18 
19    def __add__(self, other):
20        if isinstance(other, Tone):
21            return Chord (self, other)
22        elif isinstance(other, Interval):
23            new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12
24            new_tone = self.VALID_NOTES[new_tone_index]
25            return Tone(new_tone)
26        else:
27            raise TypeError("Invalid operation")
28 
29    def __sub__(self, other):
30        if isinstance(other, Tone):
31            return Interval(self&other)
32        elif isinstance(other, Interval):
33            new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12
34            new_tone = self.VALID_NOTES[new_tone_index]
35            return Tone(new_tone)
36        else:
37            raise TypeError("Invalid operation")
1138
1239
13class Interval:40class Interval:
14    SEMITONES = {41    SEMITONES = {
15        0: "unison",42        0: "unison",
16        1: "minor 2nd",43        1: "minor 2nd",
17        2: "major 2nd",44        2: "major 2nd",
18        3: "minor 3rd",45        3: "minor 3rd",
19        4: "major 3rd",46        4: "major 3rd",
20        5: "perfect 4th",47        5: "perfect 4th",
21        6: "diminished 5th",48        6: "diminished 5th",
22        7: "perfect 5th",49        7: "perfect 5th",
23        8: "minor 6th",50        8: "minor 6th",
24        9: "major 6th",51        9: "major 6th",
25        10: "minor 7th",52        10: "minor 7th",
26        11: "major 7th"53        11: "major 7th"
27    }54    }
nn55 
28    def __init__(self, number_of_semitones):56    def __init__(self, number_of_semitones):
29        self.number_of_semitones = number_of_semitones % 1257        self.number_of_semitones = number_of_semitones % 12
3058
31    def __str__(self):59    def __str__(self):
32        return self.SEMITONES[self.number_of_semitones]60        return self.SEMITONES[self.number_of_semitones]
3361
nn62    def __add__(self, other):
63        if isinstance(other, Tone):
64            raise TypeError ("Invalid operation")
65        elif isinstance(other, Interval):
66            return Interval(self.number_of_semitones + other.number_of_semitones)
67 
68    def __neg__(self):
69        return Interval(-self.number_of_semitones)
3470
35class Chord:71class Chord:
36    CHROMATIC_SCALE = Tone.VALID_NOTES72    CHROMATIC_SCALE = Tone.VALID_NOTES
3773
38    def __init__(self, main_tone, *tones):74    def __init__(self, main_tone, *tones):
39        self.main_tone = main_tone75        self.main_tone = main_tone
n40        #remove duplicates by fisr using a set and then back to a listn76        #remove duplicates by first using a set and then back to a list
41        unique_tones = {main_tone} | set(tones)77        unique_tones = list(set(tones) | {main_tone})
42        if len(unique_tones) < 2:78        if len(unique_tones) < 2:
43            raise TypeError("Cannot have a chord made of only 1 unique tone")79            raise TypeError("Cannot have a chord made of only 1 unique tone")
n44        self.tones = list(unique_tones) #this way we dont have duplicates and still can sortn80        self.tones = unique_tones
4581
46    def __str__(self):82    def __str__(self):
n47        #get the index of the 'root'n83        to_return = str(self.main_tone)
48        main_tone_idx = self.CHROMATIC_SCALE.index(self.main_tone.tone)84        main_tone_idx = self.CHROMATIC_SCALE.index(to_return)
49        sorted_tones = sorted(85        tone_str = {str(tone) for tone in self.tones}
50            self.tones,86        # sorting based on the main tone, NOT based on the indexation in Valid_Tones
51            key = lambda t: (self.CHROMATIC_SCALE.index(t.tone) - main_tone_idx) % 1287        for tone in Tone.VALID_NOTES[main_tone_idx + 1:]:
52        ) #asked for help from a friend for this (aka chat-bota) :~(88            if tone in tone_str:
53        return "-".join(str(tone) for tone in sorted_tones)89                to_return += "-" + tone
90        for tone in Tone.VALID_NOTES[:main_tone_idx]:
91            if tone in tone_str:
92                to_return += "-" + tone
93        return to_return
5494
nn95    def is_minor(self):
96        for t in self.tones:
97            if self.main_tone != t and (self.main_tone & t) == 3:  # 3 is equal to the index of minor 3rd
98                return True
99        return False
55100
n56c = Tone("C")n101    def is_major(self):
57d_sharp = Tone("D#")102        for t in self.tones:
58g = Tone("G")103            if self.main_tone != t and (self.main_tone & t) == 4:  # 4 is equal to the index of major 3rd
104                return True
105        return False
59106
n60c_minor_chord = Chord(c, d_sharp, g)n107    def is_power_chord(self):
61print(str(c_minor_chord))  # Output should be "C-D#-G"108        is_it_minor = self.is_minor()
109        is_it_major = self.is_major()
110        return not (is_it_major or is_it_minor)
62111
n63c, another_c, f = Tone("C"), Tone("C"), Tone("F")n112    def __add__(self, other):
64csus4_chord = Chord(c, f, another_c)113        if isinstance(other, Tone):
65print(str(csus4_chord))  # Output should be "C-F" -> printi C-C-F114            return Chord(self.main_tone, *(self.tones + [other]))
115        elif isinstance(other, Chord):
116            return Chord(self.main_tone, *(self.tones+other.tones))
66117
n67f, c, d, a, g = Tone("F"), Tone("C"), Tone("D"), Tone("A"), Tone("G")n118    def __sub__(self, other):
68f_sixth_ninth_chord = Chord(f, c, d, a, g)119        if isinstance(other, Tone) and other in self.tones:
69print(str(f_sixth_ninth_chord))  # Output should be "F-G-A-C-D"120            if len(self.tones) >= 3:
121                other_index = self.tones.index(other)
122                return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index]))
123            else:
124                raise TypeError("Cannot have a chord made of only 1 unique tone")
125        else:
126            raise TypeError(f"Cannot remove tone {other} from chord {self}")
70127
tt128    def transposed(self, interval):
129        to_transpose = list()
130        to_transpose.append(self.main_tone+interval)
131        for t in self.tones:
132            to_transpose.append(t + interval)
133        return Chord(*to_transpose)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op