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

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

10 точки общо

36 успешни теста
1 неуспешни теста
Код (казахте, че лимита на реда може да е 100 символа)
Скрий всички коментари

  1TONES_ORDER = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
  2
  3
  4class Tone:
  5    """Represents a musical tone."""
  6
  7    def __init__(self, name):
  8        self.name = name
  9
 10    def __str__(self):
 11        """Return the name of the tone."""
 12        return f"{self.name}"
 13
 14    def __add__(self, other):
 15        """Add a Tone or Interval to this Tone and return a new Tone or Chord."""
 16        if isinstance(other, Tone):
 17            return Chord(self, other)
 18
 19        if isinstance(other, Interval):
 20            index = (TONES_ORDER.index(self.name) + other.semitones_count) % len(TONES_ORDER)
 21            return Tone(TONES_ORDER[index])
 22
 23    def __sub__(self, other):
 24        """Subtract a Tone or Interval from this Tone, returning an Interval or a new Tone."""
 25        if isinstance(other, Tone):
 26            index = TONES_ORDER.index(self.name) - TONES_ORDER.index(other.name)
 27            return Interval(index % len(TONES_ORDER))
 28
 29        if isinstance(other, Interval):
 30            index = (TONES_ORDER.index(self.name) - other.semitones_count) % len(TONES_ORDER)
 31            return Tone(TONES_ORDER[index])
 32
 33    def __hash__(self):
 34        return hash(self.name)
 35
 36    def __eq__(self, other):
 37        if isinstance(other, Tone):
 38            return self.name == other.name
 39        return False
 40
 41
 42class Interval:
 43    """Represents a musical interval defined by the number of semitones."""
 44
 45    _INTERVAL_NAMES = {
 46        0: "unison",
 47        1: "minor 2nd",
 48        2: "major 2nd",
 49        3: "minor 3rd",
 50        4: "major 3rd",
 51        5: "perfect 4th",
 52        6: "diminished 5th",
 53        7: "perfect 5th",
 54        8: "minor 6th",
 55        9: "major 6th",
 56        10: "minor 7th",
 57        11: "major 7th"
 58    }
 59
 60    def __init__(self, semitones_count):
 61        self.semitones_count = semitones_count
 62
 63    def __str__(self):
 64        """Return the name of the interval."""
 65        return Interval._INTERVAL_NAMES[self.semitones_count % len(Interval._INTERVAL_NAMES)]
 66
 67    def __add__(self, other):
 68        """Add two intervals."""
 69        if isinstance(other, Tone):
 70            raise TypeError("Invalid operation")
 71
 72        if isinstance(other, Interval):
 73            return Interval(self.semitones_count + other.semitones_count)
 74
 75    def __sub__(self, other):
 76        if isinstance(other, Tone):
 77            raise TypeError("Invalid operation")
 78
 79    def __neg__(self):
 80        """Return the interval in opposite direction."""
 81        return Interval(-self.semitones_count)
 82
 83
 84class Chord:
 85    """Represents a musical chord built from a root tone and additional tones."""
 86
 87    def __init__(self, root_tone, *other_tones):
 88        self.root = root_tone
 89        self.other_tones = list(filter(lambda tone: tone != self.root, other_tones))
 90        if len(self.other_tones) == 0:
 91            raise TypeError("Cannot have a chord made of only 1 unique tone")
 92        self._sort_other_tones_relative_to_root()
 93
 94    def __str__(self):
 95        """Return a representation of the chord's tones."""
 96        return (
 97            f"{self.root.name}-{"-".join(map(str, self.other_tones))}"
 98            if len(self.other_tones) != 0 else self.root.name
 99        )
100
101    def _sort_other_tones_relative_to_root(self):
102        root_index = TONES_ORDER.index(self.root.name)
103        self.other_tones.sort(
104            key=lambda tone: (TONES_ORDER.index(tone.name) - root_index) % len(TONES_ORDER)
105        )
106
107    def __add__(self, other):
108        """Return a Chord with the added tones."""
109        if isinstance(other, Tone):
110            tones = set(self.other_tones) | {other}
111            return Chord(self.root, *tones)
112
113        if isinstance(other, Chord):
114            tones = set(self.other_tones) | {other.root} | set(other.other_tones)
115            return Chord(self.root, *tones)
116
117    def __sub__(self, other):
118        """Return a new Chord with the tone removed."""
119        if isinstance(other, Tone):
120            if self.root != other and other not in self.other_tones:
121                raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")
122
123            if self.root == other:
124                tones = filter(lambda tone: tone != other, self.other_tones)
125                return Chord(self.other_tones[0], *tones)
126
127            tones = filter(lambda tone: tone != other, self.other_tones)
128            return Chord(self.root, *tones)
129
130    def _check_if_tones_form_interval(self, interval_name):
131        for tone in self.other_tones:
132            if str(tone - self.root) == interval_name:
133                return True
134        return False
135
136    def is_minor(self):
137        """Check if chord is minor."""
138        return self._check_if_tones_form_interval("minor 3rd")
139
140    def is_major(self):
141        """Check if chord is major."""
142        return self._check_if_tones_form_interval("major 3rd")
143
144    def is_power_chord(self):
145        """Check if chord is a power chord."""
146        return not self.is_minor() and not self.is_major()
147
148    def transposed(self, interval):
149        """Return a new Chord transposed by a given interval."""
150        transposed_tones = map(lambda tone: tone + interval, self.other_tones)
151        return Chord(self.root + interval, *transposed_tones)

...F.................................
======================================================================
FAIL: test_chord_tone_repetition (test.TestBasicChordFunctionality.test_chord_tone_repetition)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 74, in test_chord_tone_repetition
self.assertEqual(str(a_minor_chord), "A-C-E")
AssertionError: 'A-C-C-C-C-C-C-C-C-C-C-C-C-C-C-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E' != 'A-C-E'
- A-C-C-C-C-C-C-C-C-C-C-C-C-C-C-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E-E
+ A-C-E

----------------------------------------------------------------------
Ran 37 tests in 0.001s

FAILED (failures=1)

Дискусия
Виктор Бечев
06.11.2024 17:23

Няма, и на това домашно го казваме, всичко до 103 символа е окей.
Стефан Шиваров
06.11.2024 17:19

На миналото предизвикателство ми бяхте върнали като коментар, че можем да приемем, че лимита за дължина на редовете е 100 символа, понеже 79 ми идва малко досадно :D... Надявам се не е проблем
История
Това решение има само една версия.