John O'Hagan wrote:
> 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,
As far as I can see the SeqMod and SeqTest classes don't keep any state
apart from the sequence instance. Therefore functions instead of methods
would do as well:
from functools import partial
class MySequence(object):
def __init__(self, sequence):
self.pitches = sequence[:]
def pcset(self):
return sorted(list(set([ i % 12 for i in self.pitches])))
def degrees(seq, degrees):
return all(i in seq.pcset() for i in degrees)
def rotate(seq, num):
seq.pitches = seq.pitches[-num:] + seq.pitches[:-num]
def seq_factory(sequences, tests, modifications):
for seq in sequences:
if all(test(seq) for test in tests):
for modify in modifications:
modify(seq)
yield seq
sequences = (MySequence(range(n, n+5)) for n in range(5))
tests = [
partial(degrees, degrees=[5,7]),
# ...
]
modifications = [
partial(rotate, num=3),
# ...
]
for i in seq_factory(sequences, tests, modifications):
print i.pitches
When you see that your module becomes too messy move the testing and
modifying functions into separate modules that are part of the same package.
Peter
--
http://mail.python.org/mailman/listinfo/python-list