Skip to content

Commit

Permalink
Merge pull request #1631 from TimFelixBeyer/pythonic-for-loops
Browse files Browse the repository at this point in the history
Simplify code and speed up for loops
  • Loading branch information
mscuthbert authored Jan 3, 2024
2 parents 432b116 + 022f013 commit eab94f1
Show file tree
Hide file tree
Showing 31 changed files with 288 additions and 435 deletions.
12 changes: 2 additions & 10 deletions music21/analysis/patel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# ------------------------------------------------------------------------------
from __future__ import annotations

import math
from statistics import mean, stdev
import unittest

def nPVI(streamForAnalysis):
Expand Down Expand Up @@ -98,15 +98,7 @@ def melodicIntervalVariability(streamForAnalysis, **skipKeywords):
+ 'a std-deviation of intervals (and thus a MIV)')
# summation = 0
semitoneList = [myInt.chromatic.undirected for myInt in intervalStream]
mean = 0
std = 0
for a in semitoneList:
mean = mean + a
mean = mean / totalElements
for a in semitoneList:
std = std + (a - mean) ** 2
std = math.sqrt(std / (totalElements - 1))
return 100 * (std / mean)
return 100 * (stdev(semitoneList) / mean(semitoneList))


class Test(unittest.TestCase):
Expand Down
13 changes: 4 additions & 9 deletions music21/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,9 @@ def __eq__(self, other: object):

if len(self) != len(other):
return False
sls = sorted(self)
slo = sorted(other)

for x in range(len(sls)):
if sls[x].lower() != slo[x].lower():
for eSelf, eOther in zip(sorted(self), sorted(other)):
if eSelf.lower() != eOther.lower():
return False
return True

Expand Down Expand Up @@ -3267,9 +3265,7 @@ def splitAtQuarterLength(

elif addTies and isinstance(e, chord.Chord) and isinstance(eRemain, chord.Chord):
# the last isinstance is redundant, but MyPy needs it.
for i in range(len(e.notes)):
component = e.notes[i]
remainComponent = eRemain.notes[i]
for component, remainComponent in zip(e.notes, eRemain.notes):
forceEndTieType = 'stop'
if component.tie is not None:
# the last tie of what was formally a start should
Expand Down Expand Up @@ -3352,8 +3348,7 @@ def splitByQuarterLengths(
eList = []
spannerList = [] # this does not fully work with trills over multiple splits yet.
eRemain = copy.deepcopy(self)
for qlIndex in range(len(quarterLengthList) - 1):
qlSplit = quarterLengthList[qlIndex]
for qlSplit in quarterLengthList[:-1]:
st = eRemain.splitAtQuarterLength(qlSplit,
addTies=addTies,
displayTiedAccidentals=displayTiedAccidentals)
Expand Down
35 changes: 15 additions & 20 deletions music21/beam.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,8 @@ def removeSandwichedUnbeamables(beamsList: list[Beams | None]):
beamNext = beamsList[i + 1]
else:
beamNext = None

if beamLast is None and beamNext is None:
beamsList[i] = None

beamLast = beamsList[i]

return beamsList
Expand All @@ -350,9 +348,7 @@ def mergeConnectingPartialBeams(beamsList):
16ths are not beamed by default.
'''
# sanitize two partials in a row:
for i in range(len(beamsList) - 1):
bThis = beamsList[i]
bNext = beamsList[i + 1]
for i, (bThis, bNext) in enumerate(zip(beamsList[:-1], beamsList[1:])):
if not bThis or not bNext:
continue

Expand Down Expand Up @@ -387,9 +383,7 @@ def mergeConnectingPartialBeams(beamsList):
nextBeam.direction = None

# now fix partial-lefts that follow stops:
for i in range(1, len(beamsList)):
bThis = beamsList[i]
bPrev = beamsList[i - 1]
for bThis, bPrev in zip(beamsList[1:], beamsList[:-1]):
if not bThis or not bPrev:
continue

Expand All @@ -416,17 +410,17 @@ def mergeConnectingPartialBeams(beamsList):
return beamsList

@staticmethod
def sanitizePartialBeams(beamsList):
def sanitizePartialBeams(beamsList: list[Beams | None]) -> list[Beams | None]:
'''
It is possible at a late stage to have beams that only consist of partials
or beams with a 'start' followed by 'partial/left' or possibly 'stop' followed
by 'partial/right'; beams entirely consisting of partials are removed
and the direction of irrational partials is fixed.
'''
for i in range(len(beamsList)):
if beamsList[i] is None:
for i, beamsObj in enumerate(beamsList):
if beamsObj is None:
continue
allTypes = beamsList[i].getTypes()
allTypes = beamsObj.getTypes()
# clear elements that have partial beams with no full beams:
if 'start' not in allTypes and 'stop' not in allTypes and 'continue' not in allTypes:
# nothing but partials
Expand All @@ -436,7 +430,8 @@ def sanitizePartialBeams(beamsList):
# follow a stop
hasStart = False
hasStop = False
for b in beamsList[i].beamsList:
b: Beam
for b in beamsObj.beamsList:
if b.type == 'start':
hasStart = True
continue
Expand Down Expand Up @@ -583,9 +578,9 @@ def getByNumber(self, number):
'''
if number not in self.getNumbers():
raise IndexError(f'beam number {number} cannot be accessed')
for i in range(len(self)):
if self.beamsList[i].number == number:
return self.beamsList[i]
for beam in self.beamsList:
if beam.number == number:
return beam

def getNumbers(self):
'''
Expand Down Expand Up @@ -695,10 +690,10 @@ def setByNumber(self, number, type, direction=None): # type is okay @ReservedAs
raise BeamException(f'beam type cannot be {type}')
if number not in self.getNumbers():
raise IndexError(f'beam number {number} cannot be accessed')
for i in range(len(self)):
if self.beamsList[i].number == number:
self.beamsList[i].type = type
self.beamsList[i].direction = direction
for beam in self.beamsList:
if beam.number == number:
beam.type = type
beam.direction = direction


# -----------------------------------------------------------------------------
Expand Down
44 changes: 17 additions & 27 deletions music21/duration.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,15 +862,11 @@ def ordinal(self):
>>> c.ordinal
14
'''
ordinalFound = None
for i in range(len(ordinalTypeFromNum)):
if self.type == ordinalTypeFromNum[i]:
ordinalFound = i
break
if ordinalFound is None:
try:
return ordinalTypeFromNum.index(self.type)
except ValueError:
raise DurationException(
f'Could not determine durationNumber from {ordinalFound}')
return ordinalFound
f'Could not determine durationNumber from {self.type}')


_durationTupleCacheTypeDots: dict[tuple[str, int], DurationTuple] = {}
Expand Down Expand Up @@ -2135,18 +2131,14 @@ def componentIndexAtQtrPosition(self, quarterPosition):
return self.components[-1]

currentPosition = 0.0
indexFound = None
for i in range(len(self.components)):
currentPosition = opFrac(currentPosition + self.components[i].quarterLength)
for i, component in enumerate(self.components):
currentPosition = opFrac(currentPosition + component.quarterLength)
if currentPosition > quarterPosition:
indexFound = i
break
if indexFound is None:
raise DurationException(
'Could not match quarter length within an index.')
return indexFound
return i
raise DurationException(
'Could not match quarterLength within an index.')

def componentStartTime(self, componentIndex):
def componentStartTime(self, componentIndex: int) -> float:
'''
For a valid component index value, this returns the quarter note offset
at which that component would start.
Expand All @@ -2172,15 +2164,13 @@ def componentStartTime(self, componentIndex):
IndexError: invalid component index value 3 submitted;
value must be an integer between 0 and 2
'''
if componentIndex not in range(len(self.components)):
if not (0 <= componentIndex < len(self.components)):
raise IndexError(
f'invalid component index value {componentIndex} '
+ f'submitted; value must be an integer between 0 and {len(self.components) - 1}')
components = self.components[:componentIndex]
return float(sum([c.quarterLength for c in components]))

currentPosition = 0.0
for i in range(componentIndex):
currentPosition += self.components[i].quarterLength
return currentPosition

def consolidate(self):
'''
Expand Down Expand Up @@ -2624,10 +2614,10 @@ def dotGroups(self, value: tuple[int, ...]):
if not isinstance(value, tuple):
raise TypeError('only tuple dotGroups values can be used with this method.')
# removes dots from all components...
components = list(self._components)
for i in range(len(self._components)):
components[i] = durationTupleFromTypeDots(self._components[i].type, 0)
self._components = tuple(components)
componentsGenerator = (
durationTupleFromTypeDots(component.type, 0) for component in self._components
)
self._components = tuple(componentsGenerator)

self._dotGroups = value
self._quarterLengthNeedsUpdating = True
Expand Down
11 changes: 6 additions & 5 deletions music21/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -2560,23 +2560,24 @@ def _stringToDiatonicChromatic(
dirScale = -1
else:
dirScale = 1
value_lower = value.lower()

if 'descending' in value.lower():
if 'descending' in value_lower:
value = re.sub(r'descending\s*', '', value, flags=re.RegexFlag.IGNORECASE)
dirScale = -1
elif 'ascending' in value.lower():
elif 'ascending' in value_lower:
value = re.sub(r'ascending\\s*', '', value, flags=re.RegexFlag.IGNORECASE)

# permit whole and half abbreviations
if value.lower() in ('w', 'whole', 'tone'):
if value_lower in ('w', 'whole', 'tone'):
value = 'M2'
inferred = True
elif value.lower() in ('h', 'half', 'semitone'):
elif value_lower in ('h', 'half', 'semitone'):
value = 'm2'
inferred = True

for i, ordinal in enumerate(common.musicOrdinals):
if ordinal.lower() in value.lower():
if ordinal.lower() in value_lower:
value = re.sub(fr'\s*{ordinal}\s*',
str(i),
value,
Expand Down
21 changes: 7 additions & 14 deletions music21/metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ def search(

# environLocal.printDebug(['comparing fields:', f, field])
# look for partial match in all fields
if field.lower() in uniqueName.lower():
if field in uniqueName.lower():
valueFieldPairs.append((value, uniqueName))
match = True
break
Expand All @@ -1149,21 +1149,20 @@ def search(
uniqueName, None
)

if not workId:
if workId is None:
# there is no associated grandfathered workId, don't search it
continue

# look for partial match in all fields
if field.lower() in workId.lower():
if field in workId.lower():
valueFieldPairs.append((value, workId))
match = True
break
else: # get all fields
for uniqueName, value in self.all(skipContributors=True):
if not self._isStandardUniqueName(uniqueName):
# custom metadata, don't search it
continue
valueFieldPairs.append((value, uniqueName))
# only search standard metadata
if self._isStandardUniqueName(uniqueName):
valueFieldPairs.append((value, uniqueName))

# now get all (or field-matched) contributor names, using contrib.role
# as field name, so clients can search by custom contributor role.
Expand Down Expand Up @@ -1970,13 +1969,7 @@ def _isStandardUniqueName(self, uniqueName: str) -> bool:
False
'''
prop: PropertyDescription | None = (
properties.UNIQUE_NAME_TO_PROPERTY_DESCRIPTION.get(uniqueName, None)
)
if prop is None:
return False

return True
return uniqueName in properties.UNIQUE_NAME_TO_PROPERTY_DESCRIPTION

@staticmethod
def _isStandardNamespaceName(namespaceName: str) -> bool:
Expand Down
3 changes: 1 addition & 2 deletions music21/metadata/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,11 @@ def process_parallel(jobs, processCount=None):
# end generator

@staticmethod
def process_serial(jobs):
def process_serial(jobs: list[MetadataCachingJob]):
'''
Process jobs serially.
'''
remainingJobs = len(jobs)
results = []
for job in jobs:
results, errors = job.run()
remainingJobs -= 1
Expand Down
6 changes: 3 additions & 3 deletions music21/metadata/primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,10 +862,10 @@ def __init__(self,
def __str__(self):
if isinstance(self._data, bytes):
return self._data.decode('UTF-8')
elif not isinstance(self._data, str):
return str(self._data)
else:
elif isinstance(self._data, str):
return self._data
else:
return str(self._data)

def _reprInternal(self):
return str(self)
Expand Down
Loading

0 comments on commit eab94f1

Please sign in to comment.