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

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

9 точки общо

35 успешни теста
2 неуспешни теста
Код

  1class Tone:
  2    CHROMATIC_SCALE = {
  3        "C": 0,
  4        "C#": 1,
  5        "D": 2,
  6        "D#": 3,
  7        "E": 4,
  8        "F": 5,
  9        "F#": 6,
 10        "G": 7,
 11        "G#": 8,
 12        "A": 9,
 13        "A#": 10,
 14        "B": 11,
 15    }
 16
 17    def __init__(self, value):
 18        self.value = value
 19
 20    def __str__(self):
 21        return str(self.value)
 22
 23    # Need this functions because of the unique_tones set.
 24    def __eq__(self, other):
 25        return self.value == other.value
 26
 27    def __hash__(self):
 28        return hash(self.value)
 29
 30    @property
 31    def index(self):
 32        return Tone.CHROMATIC_SCALE[self.value]
 33
 34    @staticmethod
 35    def get_tone_by_index(index):
 36        """ Helper method to get tone name by index."""
 37        for tone, tone_index in Tone.CHROMATIC_SCALE.items():
 38            if tone_index == index:
 39                return tone
 40
 41    def __add__(self, other):
 42        if isinstance(other, Tone):
 43            """Add two tones."""
 44            return Chord(self, other)
 45        elif isinstance(other, Interval):
 46            """Add tone and interval."""
 47            new_index = (self.index + other.interval) % 12
 48            new_value = Tone.get_tone_by_index(new_index)
 49            return Tone(new_value)
 50
 51    def __sub__(self, other):
 52        if isinstance(other, Tone):
 53            """Subtract two tones."""
 54            semitone_diff = (self.index - other.index) % 12
 55            return Interval(semitone_diff)
 56        else:
 57            """Subtract tone and interval."""
 58            new_index = (self.index - other.interval) % 12
 59            new_value = Tone.get_tone_by_index(new_index)
 60            return Tone(new_value)
 61
 62    def __radd__(self, other):
 63        raise TypeError("Invalid operation")
 64
 65
 66class Interval:
 67    interval_names = {
 68        0: "unison",
 69        1: "minor 2nd",
 70        2: "major 2nd",
 71        3: "minor 3rd",
 72        4: "major 3rd",
 73        5: "perfect 4th",
 74        6: "diminished 5th",
 75        7: "perfect 5th",
 76        8: "minor 6th",
 77        9: "major 6th",
 78        10: "minor 7th",
 79        11: "major 7th"
 80    }
 81
 82    def __init__(self, interval):
 83        self.interval = interval % 12
 84        # Divide the integer by 12 because I want to have a valid output also by 13 for example as given in the rule.
 85        self.name = Interval.interval_names[self.interval]
 86
 87    def __str__(self):
 88        return str(self.name)
 89
 90    def __add__(self, other):
 91        new_interval = (self.interval + other.interval) % 12
 92        return Interval(new_interval)
 93
 94    def __neg__(self):
 95        """Enable unary negation of Interval."""
 96        return Interval(-self.interval)
 97
 98
 99class Chord:
100    minor_third_interval = 3
101    major_third_interval = 4
102
103    def __init__(self, root, *tones):
104        self.root = root
105        unique_tones = {tone for tone in tones if tone != root}
106        " So we solve the problem when one of the tones is equal to root."
107
108        if len(unique_tones) < 1:
109            raise TypeError("Cannot have a chord made of only 1 unique tone")
110
111        self.tones = [self.root] + sorted(unique_tones, key=lambda tone: (tone.index - self.root.index) % 12)
112
113    def __str__(self):
114        return "-".join(str(tone) for tone in self.tones)
115
116    def is_minor(self):
117        root_index = self.root.index
118        for tone in self.tones[1:]:  # Skip the root
119            if (tone.index - root_index) % 12 == self.minor_third_interval:
120                return True
121        return False
122
123    def is_major(self):
124        root_index = self.root.index
125        for tone in self.tones[1:]:
126            if (tone.index - root_index) % 12 == self.major_third_interval:
127                return True
128        return False
129
130    def is_power_chord(self):
131        return not self.is_minor() and not self.is_major()
132
133    def __add__(self, other):
134        if isinstance(other, Tone):
135            new_tones = list(self.tones)
136            new_tones.append(other)
137            return Chord(self.root, *new_tones)
138        else:
139            combined_tones = {tone for tone in self.tones}
140            combined_tones.update(other.tones)
141            return Chord(self.root, *combined_tones)
142
143    def __sub__(self, other):
144        if other not in self.tones:
145            raise TypeError(f"Cannot remove tone {other} from chord {self}")
146
147        new_unique_tones = {tone for tone in self.tones if tone != other}
148
149        if len(new_unique_tones) < 2:
150            raise TypeError("Cannot have a chord made of only 1 unique tone")
151
152        return Chord(self.root, *new_unique_tones)
153
154    def transposed(self, interval):
155        transposed_tones = [tone + interval for tone in self.tones]
156        return Chord(transposed_tones[0], *transposed_tones[1:])

..................E........F.........
======================================================================
ERROR: 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 210, in test_add_interval_to_tone_left_side_error
Interval(2) + g
~~~~~~~~~~~~^~~
File "/tmp/solution.py", line 91, in __add__
new_interval = (self.interval + other.interval) % 12
^^^^^^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'interval'

======================================================================
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.002s

FAILED (failures=1, errors=1)

Дискусия
Валентина Петрова
04.11.2024 15:03

Ясно! Благодаря! 😁
Виктор Бечев
04.11.2024 14:07

Използва се да "маскира" достъпът до дадени данни, които изискват изчисляване, така че за външния свят да изглеждат като атрибут. Пример: ``` class Vector: ... def get_length(self): return (self.x ** 2 + self.y ** 2) ** 0.5 @property def length(self): return (self.x ** 2 + self.y ** 2) ** 0.5 v = Vector(1, 2) v.get_length() # 2.23606797749979 v.length # 2.23606797749979 ``` Второто е за предпочитане, защото прави дължината, въпреки, че трябва да се изчисли динамично, да изглежда като данни, като атрибут, а не като функция. Не е нужно да го променяш, пак ще го обясним.
Валентина Петрова
04.11.2024 13:21

Тъй като по време на лекцията не успях да разбера много добре, кога по-точно се използва декораторът @property и защо? П.С. Ако ще е част от темите на лекцията на 07.11, мога да изчакам до тогова :)
История

f1class Tone:f1class Tone:
n2    chromatic_scale = {n2    CHROMATIC_SCALE = {
3        "C": 0,3        "C": 0,
4        "C#": 1,4        "C#": 1,
5        "D": 2,5        "D": 2,
6        "D#": 3,6        "D#": 3,
7        "E": 4,7        "E": 4,
8        "F": 5,8        "F": 5,
9        "F#": 6,9        "F#": 6,
10        "G": 7,10        "G": 7,
11        "G#": 8,11        "G#": 8,
12        "A": 9,12        "A": 9,
13        "A#": 10,13        "A#": 10,
14        "B": 11,14        "B": 11,
15    }15    }
1616
17    def __init__(self, value):17    def __init__(self, value):
18        self.value = value18        self.value = value
1919
20    def __str__(self):20    def __str__(self):
21        return str(self.value)21        return str(self.value)
2222
n23    "Need this functions because of the unique_tones set."n23    Need this functions because of the unique_tones set.
24    def __eq__(self, other):24    def __eq__(self, other):
n25        return isinstance(other, Tone) and self.value == other.valuen25        return self.value == other.value
2626
27    def __hash__(self):27    def __hash__(self):
28        return hash(self.value)28        return hash(self.value)
2929
nn30    @property
30    def get_index(self):31    def index(self):
31        return Tone.chromatic_scale[self.value]32        return Tone.CHROMATIC_SCALE[self.value]
3233
33    @staticmethod34    @staticmethod
34    def get_tone_by_index(index):35    def get_tone_by_index(index):
35        """ Helper method to get tone name by index."""36        """ Helper method to get tone name by index."""
n36        for tone, tone_index in Tone.chromatic_scale.items():n37        for tone, tone_index in Tone.CHROMATIC_SCALE.items():
37            if tone_index == index:38            if tone_index == index:
38                return tone39                return tone
3940
40    def __add__(self, other):41    def __add__(self, other):
41        if isinstance(other, Tone):42        if isinstance(other, Tone):
42            """Add two tones."""43            """Add two tones."""
43            return Chord(self, other)44            return Chord(self, other)
44        elif isinstance(other, Interval):45        elif isinstance(other, Interval):
45            """Add tone and interval."""46            """Add tone and interval."""
n46            new_index = (self.get_index() + other.interval) % 12n47            new_index = (self.index + other.interval) % 12
47            new_value = Tone.get_tone_by_index(new_index)48            new_value = Tone.get_tone_by_index(new_index)
48            return Tone(new_value)49            return Tone(new_value)
4950
50    def __sub__(self, other):51    def __sub__(self, other):
51        if isinstance(other, Tone):52        if isinstance(other, Tone):
52            """Subtract two tones."""53            """Subtract two tones."""
n53            semitone_diff = (self.get_index() - other.get_index()) % 12n54            semitone_diff = (self.index - other.index) % 12
54            return Interval(semitone_diff)55            return Interval(semitone_diff)
55        else:56        else:
56            """Subtract tone and interval."""57            """Subtract tone and interval."""
n57            new_index = (self.get_index() - other.interval) % 12n58            new_index = (self.index - other.interval) % 12
58            new_value = Tone.get_tone_by_index(new_index)59            new_value = Tone.get_tone_by_index(new_index)
59            return Tone(new_value)60            return Tone(new_value)
6061
61    def __radd__(self, other):62    def __radd__(self, other):
62        raise TypeError("Invalid operation")63        raise TypeError("Invalid operation")
6364
6465
65class Interval:66class Interval:
66    interval_names = {67    interval_names = {
67        0: "unison",68        0: "unison",
68        1: "minor 2nd",69        1: "minor 2nd",
69        2: "major 2nd",70        2: "major 2nd",
70        3: "minor 3rd",71        3: "minor 3rd",
71        4: "major 3rd",72        4: "major 3rd",
72        5: "perfect 4th",73        5: "perfect 4th",
73        6: "diminished 5th",74        6: "diminished 5th",
74        7: "perfect 5th",75        7: "perfect 5th",
75        8: "minor 6th",76        8: "minor 6th",
76        9: "major 6th",77        9: "major 6th",
77        10: "minor 7th",78        10: "minor 7th",
78        11: "major 7th"79        11: "major 7th"
79    }80    }
8081
81    def __init__(self, interval):82    def __init__(self, interval):
82        self.interval = interval % 1283        self.interval = interval % 12
n83        " Divide the integer by 12 because I want to have a valid output also by 13 for example as given in the rule."n84        # Divide the integer by 12 because I want to have a valid output also by 13 for example as given in the rule.
84        self.name = Interval.interval_names.get(self.interval, "unknown")85        self.name = Interval.interval_names[self.interval]
8586
86    def __str__(self):87    def __str__(self):
87        return str(self.name)88        return str(self.name)
8889
89    def __add__(self, other):90    def __add__(self, other):
90        new_interval = (self.interval + other.interval) % 1291        new_interval = (self.interval + other.interval) % 12
91        return Interval(new_interval)92        return Interval(new_interval)
9293
93    def __neg__(self):94    def __neg__(self):
94        """Enable unary negation of Interval."""95        """Enable unary negation of Interval."""
95        return Interval(-self.interval)96        return Interval(-self.interval)
9697
9798
98class Chord:99class Chord:
nn100    minor_third_interval = 3
101    major_third_interval = 4
102 
99    def __init__(self, root, *tones):103    def __init__(self, root, *tones):
100        self.root = root104        self.root = root
101        unique_tones = {tone for tone in tones if tone != root}105        unique_tones = {tone for tone in tones if tone != root}
102        " So we solve the problem when one of the tones is equal to root."106        " So we solve the problem when one of the tones is equal to root."
103107
104        if len(unique_tones) < 1:108        if len(unique_tones) < 1:
105            raise TypeError("Cannot have a chord made of only 1 unique tone")109            raise TypeError("Cannot have a chord made of only 1 unique tone")
106110
n107        self.tones = [self.root] + sorted(unique_tones, key=lambda tone: (tone.get_index() - self.root.get_index()) % 12)n111        self.tones = [self.root] + sorted(unique_tones, key=lambda tone: (tone.index - self.root.index) % 12)
108112
109    def __str__(self):113    def __str__(self):
110        return "-".join(str(tone) for tone in self.tones)114        return "-".join(str(tone) for tone in self.tones)
111115
112    def is_minor(self):116    def is_minor(self):
n113        minor_third_interval = 3n
114        root_index = self.root.get_index()117        root_index = self.root.index
115        for tone in self.tones[1:]:  # Skip the root118        for tone in self.tones[1:]:  # Skip the root
n116            if (tone.get_index() - root_index) % 12 == minor_third_interval:n119            if (tone.index - root_index) % 12 == self.minor_third_interval:
117                return True120                return True
118        return False121        return False
119122
120    def is_major(self):123    def is_major(self):
n121        major_third_interval = 4n
122        root_index = self.root.get_index()124        root_index = self.root.index
123        for tone in self.tones[1:]:125        for tone in self.tones[1:]:
n124            if (tone.get_index() - root_index) % 12 == major_third_interval:n126            if (tone.index - root_index) % 12 == self.major_third_interval:
125                return True127                return True
126        return False128        return False
127129
128    def is_power_chord(self):130    def is_power_chord(self):
129        return not self.is_minor() and not self.is_major()131        return not self.is_minor() and not self.is_major()
130132
131    def __add__(self, other):133    def __add__(self, other):
132        if isinstance(other, Tone):134        if isinstance(other, Tone):
133            new_tones = list(self.tones)135            new_tones = list(self.tones)
134            new_tones.append(other)136            new_tones.append(other)
135            return Chord(self.root, *new_tones)137            return Chord(self.root, *new_tones)
136        else:138        else:
137            combined_tones = {tone for tone in self.tones}139            combined_tones = {tone for tone in self.tones}
138            combined_tones.update(other.tones)140            combined_tones.update(other.tones)
139            return Chord(self.root, *combined_tones)141            return Chord(self.root, *combined_tones)
140142
141    def __sub__(self, other):143    def __sub__(self, other):
142        if other not in self.tones:144        if other not in self.tones:
143            raise TypeError(f"Cannot remove tone {other} from chord {self}")145            raise TypeError(f"Cannot remove tone {other} from chord {self}")
144146
145        new_unique_tones = {tone for tone in self.tones if tone != other}147        new_unique_tones = {tone for tone in self.tones if tone != other}
146148
147        if len(new_unique_tones) < 2:149        if len(new_unique_tones) < 2:
148            raise TypeError("Cannot have a chord made of only 1 unique tone")150            raise TypeError("Cannot have a chord made of only 1 unique tone")
149151
150        return Chord(self.root, *new_unique_tones)152        return Chord(self.root, *new_unique_tones)
151153
152    def transposed(self, interval):154    def transposed(self, interval):
153        transposed_tones = [tone + interval for tone in self.tones]155        transposed_tones = [tone + interval for tone in self.tones]
154        return Chord(transposed_tones[0], *transposed_tones[1:])156        return Chord(transposed_tones[0], *transposed_tones[1:])
155157
t156 t
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op