J
John O'Hagan
I want to know the best way to organise a bunch of functions designed to
operate on instances of a given class without cluttering the class itself with
a bunch of unrelated methods.
What I've done is make what I think are called helper classes, each of which
are initialized with an instance of the main class and has methods which are
all of the same type (insofar as they return a boolean, or modify the object
in place, or whatever).
I'm not sure if I'm on the right track here design-wise. Maybe this could be
better done with inheritance (not my forte), but my first thought is that no,
the helper classes (if that's what they are) are not actually a type of the
main class, but are auxiliary to it.
Here's what I've done:
I have a class MySequence which is initialized with a number sequence (in a
list), which has a bunch of methods which deal with various (musical)
properties of the sequence. Toy example:
class MySequence(object):
"""MySequence, a representation musical sequences as numbers.
Its methods return various characteristics of the sequence."""
def __init__(self, sequence):
self.pitches = sequence[:]
def pcset(self):
"""Example method: The pitch class set
derived from the sequence"""
return sorted(list(set([ i % 12 for i in self.pitches])))
A generator function spits out MySequence objects, and I want to filter them
(i.e. reject those which do not meet certain criteria) and then be able to
modify them in various ways. For that I have two classes; toy examples:
class SeqTest(object):
"""SeqTest, initialized with a MySequence object. Its methods
return the boolean result of tests against the Sequence object."""
def __init__(self, myseq_obj):
self.seq = myseq_obj
def degrees(self, degrees):
"""Example method: Test for certain members, passed as list"""
return all(i in self.seq.pcset() for i in degrees)
class SeqMod(object):
"""A SeqMod object's methods modify in place
the MySequence object with which it is initialized """
def __init__(self, myseq_obj):
self.seq = myseq_obj
def rotate(self, num):
"""Example method: Rotate pitches by n steps"""
self.seq.pitches = self.seq.pitches[-num:] + self.seq.pitches[:-num]
And here is a toy version of how I'm using them with the generator:
def seq_factory(generator_func, test_opts, mod_opts):
"""Yields Sequence objects, filtered and modified.
Opts are dictionaries."""
for sequence in generator_func:
seq = MySequence(sequence)
tester = SeqTest(seq)
if any (not getattr(tester, opt)(value)
for opt, value in test_opts.items()):
continue
modifier = SeqMod(seq)
for opt, value in mod_opts.items():
getattr(modifier, opt)(value)
yield seq
Used, say, like this:
generator_func = (range(n, n+5) for n in range(5))
test_opts = {'degrees': [5,7]}
mod_opts = {'rotate': 3}
for i in seq_factory(generator_func, test_opts, mod_opts):
print i.pitches
Which yields:
[5, 6, 7, 3, 4]
[6, 7, 8, 4, 5]
It actually works well, so there's no real problem apart from wanting to know
if this is a good way to do what I want.
Thanks for any wise words,
John
operate on instances of a given class without cluttering the class itself with
a bunch of unrelated methods.
What I've done is make what I think are called helper classes, each of which
are initialized with an instance of the main class and has methods which are
all of the same type (insofar as they return a boolean, or modify the object
in place, or whatever).
I'm not sure if I'm on the right track here design-wise. Maybe this could be
better done with inheritance (not my forte), but my first thought is that no,
the helper classes (if that's what they are) are not actually a type of the
main class, but are auxiliary to it.
Here's what I've done:
I have a class MySequence which is initialized with a number sequence (in a
list), which has a bunch of methods which deal with various (musical)
properties of the sequence. Toy example:
class MySequence(object):
"""MySequence, a representation musical sequences as numbers.
Its methods return various characteristics of the sequence."""
def __init__(self, sequence):
self.pitches = sequence[:]
def pcset(self):
"""Example method: The pitch class set
derived from the sequence"""
return sorted(list(set([ i % 12 for i in self.pitches])))
A generator function spits out MySequence objects, and I want to filter them
(i.e. reject those which do not meet certain criteria) and then be able to
modify them in various ways. For that I have two classes; toy examples:
class SeqTest(object):
"""SeqTest, initialized with a MySequence object. Its methods
return the boolean result of tests against the Sequence object."""
def __init__(self, myseq_obj):
self.seq = myseq_obj
def degrees(self, degrees):
"""Example method: Test for certain members, passed as list"""
return all(i in self.seq.pcset() for i in degrees)
class SeqMod(object):
"""A SeqMod object's methods modify in place
the MySequence object with which it is initialized """
def __init__(self, myseq_obj):
self.seq = myseq_obj
def rotate(self, num):
"""Example method: Rotate pitches by n steps"""
self.seq.pitches = self.seq.pitches[-num:] + self.seq.pitches[:-num]
And here is a toy version of how I'm using them with the generator:
def seq_factory(generator_func, test_opts, mod_opts):
"""Yields Sequence objects, filtered and modified.
Opts are dictionaries."""
for sequence in generator_func:
seq = MySequence(sequence)
tester = SeqTest(seq)
if any (not getattr(tester, opt)(value)
for opt, value in test_opts.items()):
continue
modifier = SeqMod(seq)
for opt, value in mod_opts.items():
getattr(modifier, opt)(value)
yield seq
Used, say, like this:
generator_func = (range(n, n+5) for n in range(5))
test_opts = {'degrees': [5,7]}
mod_opts = {'rotate': 3}
for i in seq_factory(generator_func, test_opts, mod_opts):
print i.pitches
Which yields:
[5, 6, 7, 3, 4]
[6, 7, 8, 4, 5]
It actually works well, so there's no real problem apart from wanting to know
if this is a good way to do what I want.
Thanks for any wise words,
John