Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Equality changes -- hierarchy #1466

Merged
merged 6 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/source/developerReference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Developer Reference

usingGit
musicxmlTest

startingOver


Documentation and tests in progress
Expand Down
55 changes: 55 additions & 0 deletions documentation/source/developerReference/startingOver.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "45a1e554",
"metadata": {},
"source": [
"# Music21's Mistakes\n",
"\n",
"So, you're a developer. Maybe you hate Python or you hate `music21` or you think (whenever you read this) that `music21` was cool for its time, but it's time to create a new system from scratch.\n",
"\n",
"I'm never going to do that: I know about the [Second System Effect](https://en.wikipedia.org/wiki/Second-system_effect) and know that I couldn't do it right.\n",
"\n",
"But if you're interested, here are the mistakes I made in `music21` that I know of that are too late to change now, but that I'd avoid if I were you:\n",
"\n",
"1. Most objects should be immutable. Maybe Streams can't be, but definitely Pitches, Accidentals, Durations, etc. Then they would be hashable and equality operators would work the way Python and most other languages intend them to be. But as it is, we have a situation where we need to know that Streams compare on identity while lists of notes compare on equality. If you don't have a handle on the difference between the identity and equality, do yourself a favor and become an expert before writing your system. I didn't know and now things are awful in this respect.\n",
"\n",
"2. Because of point 1, I probably would store objects and sites separately and return them as a separate composite object; this would eliminate 90% of the deepcopying happening in `music21`.\n",
"\n",
"3. I borrowed `-` for flat from Humdrum thinking that there would otherwise be a lot of conflicts between octave signatures for B, like 'BBB' and 'bbb' and flats. It turns out that hasn't been much of a problem.\n",
"\n",
"4. Follow Liskov Substitution Principle. Violations of it are all over music21, and that's been an issue as computers need to infer type better. \n",
"\n",
"5. Use fewer properties except when they are fast to compute, and never let a property have a side effect. Modern IDEs are constantly querying all properties to see what that might contain. This is why `.flat` had to become `.flatten()`.\n",
"\n",
"6. Keep the core of the toolkit small and encourage an ecosystem of other add-ons.\n",
"\n",
"7. Don't try to encode every exception to standard music that your musicologist brain can conceive of. Put those in the `.editorial` or other special purpose attribute. \n",
"\n",
"There will be more here as I think of it. -- Michael Scott Asato Cuthbert, 2022 October."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
4 changes: 2 additions & 2 deletions music21/analysis/reduceChords.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def _getIntervalClassSet(pitches):
return frozenset(result)

def _iterateElementsPairwise(self, inputStream):
elementBuffer = []
elementBuffer = collections.deque()
prototype = (
chord.Chord,
note.Note,
Expand All @@ -193,7 +193,7 @@ def _iterateElementsPairwise(self, inputStream):
elementBuffer.append(element)
if len(elementBuffer) == 2:
yield tuple(elementBuffer)
elementBuffer.pop(0)
elementBuffer.popleft()

# PUBLIC METHODS #
def alignHockets(self, scoreTree):
Expand Down
81 changes: 35 additions & 46 deletions music21/articulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,41 @@ class Articulation(base.Music21Object):
>>> x.style.absoluteY = 20
>>> x.displayText = '>'

Equality
--------
Equality of articulations is based only on the class, as other attributes are independent
of context and deployment.

>>> at1 = articulations.StrongAccent()
>>> at2 = articulations.StrongAccent()
>>> at1.placement = 'above'
>>> at2.placement = 'below'
>>> at1 == at2
True

Comparison between classes and with the object itself behaves as expected:

>>> at3 = articulations.Accent()
>>> at4 = articulations.Staccatissimo()
>>> at1 == at3
False
>>> at4 == at4
True

OMIT_FROM_DOCS

>>> at5 = articulations.Staccato()
>>> at6 = articulations.Spiccato()
>>> [at1, at4, at3] == [at1, at4, at3]
True
>>> [at1, at2, at3] == [at2, at3, at1]
False
>>> {at1, at2, at3} == {at2, at3, at1}
True
>>> at6 == True
False

This is in OMIT
'''
_styleClass: type[style.Style] = style.TextStyle

Expand Down Expand Up @@ -138,52 +173,6 @@ class name without the leading letter lowercase.
className = self.__class__.__name__
return common.camelCaseToHyphen(className, replacement=' ')

# def __eq__(self, other):
# '''
# Equality. Based only on the class name,
# as other attributes are independent of context and deployment.
#
#
# >>> at1 = articulations.StrongAccent()
# >>> at2 = articulations.StrongAccent()
# >>> at1.placement = 'above'
# >>> at2.placement = 'below'
# >>> at1 == at2
# True
#
#
# Comparison between classes and with the object itself behaves as expected
#
#
# >>> at3 = articulations.Accent()
# >>> at4 = articulations.Staccatissimo()
# >>> at1 == at3
# False
# >>> at4 == at4
# True
#
#
# OMIT_FROM_DOCS
#
# >>> at5 = articulations.Staccato()
# >>> at6 = articulations.Spiccato()
# >>> [at1, at4, at3] == [at1, at4, at3]
# True
# >>> [at1, at2, at3] == [at2, at3, at1]
# False
# >>> set([at1, at2, at3]) == set([at2, at3, at1])
# True
# >>> at6 == None
# False
# '''
# # checks pitch.octave, pitch.accidental, uses Pitch.__eq__
# if other == None or not isinstance(other, Articulation):
# return False
# elif self.__class__ == other.__class__:
# return True
# return False
#

def _getVolumeShift(self):
return self._volumeShift

Expand Down
2 changes: 2 additions & 0 deletions music21/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ class Barline(base.Music21Object):

classSortOrder = -5

equalityAttributes = ('type', 'pause', 'location')

def __init__(self,
type=None, # pylint: disable=redefined-builtin
location=None,
Expand Down
Loading