-
-
Notifications
You must be signed in to change notification settings - Fork 53
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
Add _apply_list_op, each, map, filter to ComplexBase #47
base: main
Are you sure you want to change the base?
Conversation
Related to #32. |
The functions For for obj in music.objects():
fix_pitches(obj) (We cannot achieve the |
|
Oops. You are right. for list_ in music.lists():
list_[:] = map(func, list_) Similarly, for for list_ in music.lists():
list_[:] = [obj for obj in list_ if is_in_range(obj, a=4, b=8)] Neither is that pythonic though. |
Also, isn't |
|
It seems that |
Yes, a bit, but
I was thinking about that. But the thing is that it accepts a function with 3 different parameters (of which I'm currently using only 2 actually...) and so (1) it can't be used with existing functions like built-in |
It does behave like a map on all the lists but does not return anything. >>> a, b, c = [0], [1], [2]
>>> map(lambda x: x.append(-1), [a,b,c])
<map at 0x23c92c3bf88>
>>> print(a, b, c) # has no effect as map returns an iterator
[0] [1] [2]
>>> list(map(lambda x: x.append(-1), [a,b,c]))
[None, None, None]
>>> print(a, b, c)
[0, -1] [1, -1] [2, -1] |
In fact, |
I do think implementing |
If it weren't for the intricacies of adjusting durations... |
…ace each with flat
OK, I removed the methods. I replaced |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The implementation of
_traverse_lists
looks good to me. - I am not a fan of
flat
in music21. We should use a more obvious and pythonic name. For now, I can only come up withlists()
andlists_with_names()
. There should be better options. - I already fixed remove_invalid() makes object even more invalid #49 by 8899576, but I am not sure which would be faster
muspy/base.py
Outdated
@property | ||
def flat(self) -> Iterable: | ||
"""A flat representation of this object. Iterating over it | ||
yields all items in all list attributes inside this object | ||
(recursively). Non-list attributes are not included. | ||
""" | ||
return self._flat | ||
|
||
def _flat_generator(self) -> Iterable: | ||
value: list | ||
for _, _, value in self._traverse_lists(attr=None, recursive=True): | ||
yield from value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be problematic as it will keep reusing the generator created at __init__
. We probably don't want this behavior. Also, I am not a fan of having flat
as a property. Could we simply make it Music.lists()
, which is rather straightforward from its name except that it's recursive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I didn't realize that.
How about this? flat
is now reusable, and it's always the same object, so it's not a problem to have it as a property.
If you still insist on it being a function, why not call it e.g. walk_lists
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just want it to behave more like dict.items()
or dict.values()
rather than numpy.ndarray.flat
or music21.stream.Stream.flat
. It's quite common in Python to have a function that returns an iterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Is walk_lists()
OK then? The name should give a hint that the return value is good only for (one) iteration.
BTW dict.items()
and dict.values()
also return objects that support more than just iteration (e.g. len()
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, lists()
suggests that it would iterate over lists. But my flat
iterates over list items, not lists themselves. So we need a better name. I think flat()
(just changing the property to a function) or walk_lists()
is OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it list_items()
and added list()
to iterate over the actual lists.
yield from item._traverse_lists( # type: ignore | ||
attr=None, recursive=recursive) | ||
|
||
yield (attr, attr_type, getattr(self, attr)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A return signature of either the value (like dict.values
) or a tuple of the key and the value(attr, value)
(like dict.items
) would be more straightforward. Maybe we can make them two separate methods.
But if we do this, we can no longer make remove_invalid
, remove_duplicate
and sort
work using this, right?
Or we could keep this unchanged and have two public wrappers lists
and lists_with_names
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I would definitely keep this and internal function.
if isclass(attr_type) and issubclass(attr_type, Base): | ||
value[:] = [item for item in value # type: ignore | ||
if (isinstance(item, attr_type) | ||
and cast(Base, item).is_valid())] | ||
else: | ||
value[:] = [item for item in value # type: ignore | ||
if isinstance(item, attr_type)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type checking can be simplified if we change the return signature into Iterator[Tuple[str, List]]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there must be a bug in mypy, it was complaining about things whose type is actually known...
OK, I replaced Example: >>> list(m.lists())
[('tempos', []),
('key_signatures', []),
('time_signatures', []),
('downbeats', []),
('lyrics', []),
('annotations', []),
('tracks',
[Track(program=33, is_drum=False, name='BB Bass', notes=[Note(time=0, pitch=34, duration=12, velocity=59), Note(time=18, pitch=43, duration=17, velocity=74), Note(time=36, pitch=39, duration=5, velocity=62), ...])])]
>>> list(m.list_items())
[Track(program=33, is_drum=False, name='BB Bass', notes=[Note(time=0, pitch=34, duration=12, velocity=59), Note(time=18, pitch=43, duration=17, velocity=74), Note(time=36, pitch=39, duration=5, velocity=62), ...])]
>>> list(m.lists(recursive=True))
[('tempos', []),
('key_signatures', []),
('time_signatures', []),
('downbeats', []),
('lyrics', []),
('annotations', []),
('notes',
[Note(time=0, pitch=34, duration=12, velocity=59),
Note(time=18, pitch=43, duration=17, velocity=74),
Note(time=36, pitch=39, duration=5, velocity=62),
...]),
('chords', []),
('lyrics', []),
('annotations', []),
('tracks',
[Track(program=33, is_drum=False, name='BB Bass', notes=[Note(time=0, pitch=34, duration=12, velocity=59), Note(time=18, pitch=43, duration=17, velocity=74), Note(time=36, pitch=39, duration=5, velocity=62), ...])])]
>>> list(m.list_items(recursive=True))
[Note(time=0, pitch=34, duration=12, velocity=59),
Note(time=18, pitch=43, duration=17, velocity=74),
Note(time=36, pitch=39, duration=5, velocity=62),
...,
Track(program=33, is_drum=False, name='BB Bass', notes=[Note(time=0, pitch=34, duration=12, velocity=59), Note(time=18, pitch=43, duration=17, velocity=74), Note(time=36, pitch=39, duration=5, velocity=62), ...])] |
_apply_list_op
method that recursively applies a list operation to list attributes.remove_duplicate
,remove_invalid
andsort
using this method (seems to fix remove_invalid() makes object even more invalid #49).each
,map
andfilter
functions.Example for
filter
:Example for
each
: