views:

154

answers:

3

Class hierarchies and constructors are related. Parameters from a child class need to be passed to their parent.

So, in Python, we end up with something like this:

class Parent(object):
    def __init__(self, a, b, c, ka=None, kb=None, kc=None):
        # do something with a, b, c, ka, kb, kc

class Child(Parent):
    def __init__(self, a, b, c, d, e, f, ka=None, kb=None, kc=None, kd=None, ke=None, kf=None):
        super(Child, self).__init__(a, b, c, ka=ka, kb=kb, kc=kc)
        # do something with d, e, f, kd, ke, kf

Imagine this with a dozen child classes and lots of parameters. Adding new parameters becomes very tedious.

Of course one can dispense with named parameters completely and use *args and **kwargs, but that makes the method declarations ambiguous.

Is there a pattern for elegantly dealing with this in Python (2.6)?

By "elegantly" I mean I would like to reduce the number of times the parameters appear. a, b, c, ka, kb, kc all appear 3 times: in the Child constructor, in the super() call to Parent, and in the Parent constructor.

Ideally, I'd like to specify the parameters for Parent's init once, and in Child's init only specify the additional parameters.

I'd like to do something like this:

class Parent(object):
    def __init__(self, a, b, c, ka=None, kb=None, kc=None):
        print 'Parent: ', a, b, c, ka, kb, kc

class Child(Parent):
    def __init__(self, d, e, f, kd='d', ke='e', kf='f', *args, **kwargs):
        super(Child, self).__init__(*args, **kwargs)
        print 'Child: ', d, e, f, kd, ke, kf

x = Child(1, 2, 3, 4, 5, 6, ka='a', kb='b', kc='c', kd='d', ke='e', kf='f')

This unfortunately doesn't work, since 4, 5, 6 end up assigned to kd, ke, kf.

Is there some elegant python pattern for accomplishing the above?

+2  A: 

Well, the only solution I could see is using a mixture of listed variables as well as *args and **kwargs, as such:

class Parent(object):
    def __init__(self, a, b, c, ka=None, kb=None, kc=None):
     pass

class Child(Parent):
    def __init__(self, d, e, f, *args, kd=None, ke=None, kf=None, **kwargs):
     Parent.__init__(self, *args, **kwargs)
     pass

This way, you could see which parameters are required by each of the classes, but without having to re-type them.

One thing to note is that you lose your desired ordering (a, b, c, d, e, f) as it becomes (d, e, f, a, b, c). I'm not sure if there's a way to have the *args before the other non-named parameters.

Adam
That was my first thought as well, but python seems to require the *args and **kwargs to be last, at least in 2.6.
markv
Hrm, yeah. I suppose lock it away as one of those possibilities for when you move to Python 3.
Adam
+5  A: 

"dozen child classes and lots of parameters" sounds like a problem irrespective of parameter naming.

I suspect that a little refactoring can peel out some Strategy objects that would simplify this hierarchy and make the super-complex constructors go away.

S.Lott
I've already factored out a few strategy objects, but your comment pointed me at some reading that showed me how to stream line what I've already done. Thanks!
markv
+1  A: 

I try to group the parameters into their own objects, e.g, instead of passing sourceDirectory, targetDirectory, temporaryDirectory, serverName, serverPort, I'd have a DirectoryContext and ServerContext objects.

If the context objects start having more behavior or logic it might lead to the strategy objects mentioned in here.

maccullt