Adding modified methods from another class without subclassing
research at johnohagan.com
Mon Aug 22 07:04:45 CEST 2011
I have a class like this:
def __init__(self, *seq, c=12):
self.__c = c
self.__pc = sorted(set([i % __c for i in seq]))
self.order = ([[self.__pc.index(i % __c), i // __c] for i in seq])
#other calculated attributes
return [self.__pc[i] + i * self.__c for i in self.order]
The "pitches" attribute initially reconstructs the "seq" arguments but can be modified by writing to the "order" attribute.
The "pitches" attribute represents the instances and as such I found myself adding a lot of methods like:
def __getitem__(self, index):
and so on, and calling a lot of list methods on the "pitches" attribute of MySeq instances elsewhere. I thought of making MySeq a subclass of list with "pitches" as its contents, but then I would have had to override a lot of methods case-by-case, for example to ensure that any alterations to "pitches" were reflected in the other calculated attributes.
So I wrote this function which takes a method, modifies it to apply to an instance attribute, and takes care of any quirks:
def listmeth_to_attribute(meth, attr):
def new_meth(inst, *args):
#ensure comparison operators work:
args = [getattr(i, attr) if isinstance(i, inst.__class__)
else i for i in args]
reference = getattr(inst, attr)
test = reference[:]
result = meth(test, *args)
#ensure instance is reinitialised
#if attribute has been changed:
if test != reference:
#ensure slices are of same class
if isinstance(result, meth.__objclass__):
result = inst.__class__(*result)
and this decorator to apply this function to all the list methods and add them to MySeq:
def add_mod_methods(source_cls, modfunc, modfunc_args, *overrides):
"""Overides = any methods in target to override from source"""
for name, meth in vars(source_cls).items():
if name not in dir(target_cls) or name in overrides:
setattr(target_cls, name, modfunc(meth, *modfunc_args))
a kind of DIY single inheritance, used like this:
@add_mod_methods(list, listmeth_to_attribute, ('pitches',), '__repr__')
Now I can call list methods transparently on MySeq instances, like subclassing but without all the overriding. If this works it will simplify a lot of code in my project.
But the fact that I haven't seen this approach before increases the likelihood it may not be a good idea. I can almost hear the screams of "No, don't do that!" or the sound of me slapping my forehead when someone says "Why don't you just...". So before I put it in, I'd appreciate any comments, warnings, criticisms, alternatives etc..
More information about the Python-list