diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index e1bdab51f..fa7551973 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -286,12 +286,12 @@ def _extractNoteReductiveEvent(self, n, infoDict=None, removeAfterParsing=True): return removalIndices = [] - if m.hasElement(n): + if n in m: offset = n.getOffsetBySite(m) else: # it is in a Voice offset = 0.0 for v in m.voices: - if v.hasElement(n): + if n in v: offset = n.getOffsetBySite(v) diff --git a/music21/base.py b/music21/base.py index 124eb593c..f58f7f7af 100644 --- a/music21/base.py +++ b/music21/base.py @@ -1256,7 +1256,7 @@ def purgeOrphans(self, excludeStorageStreams=True) -> None: # of the site does not actually have this Music21Object in # its elements list, it is an orphan and should be removed # note: this permits non-site context Streams to continue - if s.isStream and not s.hasElement(self): + if s.isStream and self not in s: if excludeStorageStreams: # only get those that are not Storage Streams if ('SpannerStorage' not in s.classes diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index 2b08ed999..5701785f8 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -1193,8 +1193,8 @@ def testSerializationScaffoldA(self): sf.setupSerializationScaffold() # test safety - self.assertTrue(s2.hasElement(n1)) - self.assertTrue(s1.hasElement(n1)) + self.assertTrue(n1 in s2) + self.assertTrue(n1 in s1) def testJSONPickleSpanner(self): from music21 import converter diff --git a/music21/stream/base.py b/music21/stream/base.py index a7a3e26f4..aaa732754 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -783,7 +783,7 @@ def last(self) -> M21ObjType | None: except IndexError: return None - def __contains__(self, el): + def __contains__(self, el: base.Music21Object) -> bool: ''' Returns True if `el` is in the stream (compared with Identity) and False otherwise. @@ -811,8 +811,11 @@ def __contains__(self, el): >>> nC2 in s.elements True ''' - return (any(sEl is el for sEl in self._elements) - or any(sEl is el for sEl in self._endElements)) + # Should be the fastest implementation of this naive check, compare with + # https://stackoverflow.com/questions/44802682/python-any-unexpected-performance + return (id(el) in self._offsetDict + or any(True for sEl in self._elements if sEl is el) + or any(True for sEl in self._endElements if sEl is el)) @property def elements(self) -> tuple[M21ObjType, ...]: @@ -1434,6 +1437,8 @@ def mergeAttributes(self, other: base.Music21Object): if hasattr(other, attr): setattr(self, attr, getattr(other, attr)) + @common.deprecated('v10', 'v11', 'Use `el in stream` instead of ' + '`stream.hasElement(el)`') def hasElement(self, obj: base.Music21Object) -> bool: ''' Return True if an element, provided as an argument, is contained in @@ -1449,16 +1454,7 @@ def hasElement(self, obj: base.Music21Object) -> bool: >>> s.hasElement(n1) True ''' - if id(obj) in self._offsetDict: - return True - - for e in self._elements: - if e is obj: # pragma: no cover - return True - for e in self._endElements: - if e is obj: # pragma: no cover - return True - return False + return obj in self def hasElementOfClass(self, className, forceFlat=False): ''' @@ -2013,7 +2009,7 @@ def _deepcopySubclassable(self: StreamType, # must manually add elements to new Stream for e in self._elements: # environLocal.printDebug(['deepcopy()', e, 'old', old, 'id(old)', id(old), - # 'new', new, 'id(new)', id(new), 'old.hasElement(e)', old.hasElement(e), + # 'new', new, 'id(new)', id(new), 'e in old', e in old, # 'e.activeSite', e.activeSite, 'e.getSites()', e.getSites(), 'e.getSiteIds()', # e.getSiteIds()], format='block') # diff --git a/music21/test/test_base.py b/music21/test/test_base.py index de1bc0df6..7f0ea16fc 100644 --- a/music21/test/test_base.py +++ b/music21/test/test_base.py @@ -570,14 +570,14 @@ def testStoreLastDeepCopyOf(self): n2 = copy.deepcopy(n1) self.assertEqual(id(n2.derivation.origin), id(n1)) - def testHasElement(self): + def testContains(self): n1 = note.Note() s1 = stream.Stream() s1.append(n1) s2 = copy.deepcopy(s1) n2 = s2[0] # this is a new instance; not the same as n1 - self.assertFalse(s2.hasElement(n1)) - self.assertTrue(s2.hasElement(n2)) + self.assertFalse(n1 in s2) + self.assertTrue(n2 in s2) self.assertFalse(s1 in n2.sites) self.assertTrue(s2 in n2.sites) @@ -622,7 +622,7 @@ class Mock: s.insert(i, el) for ew in storage: - self.assertTrue(s.hasElement(ew)) + self.assertTrue(ew in s) match = [e.getOffsetBySite(s) for e in storage] self.assertEqual(match, [0.0, 1.0]) @@ -648,7 +648,7 @@ def getnchannels(self): s.insert(i, el) for ew in storage: - self.assertTrue(s.hasElement(ew)) + self.assertTrue(ew in s) matchOffset = [] matchBeatStrength = []