views:

100

answers:

4

I have a class:

class A(object):
    def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
        #do some init stuff

And I have a subclass which needs one extra arg (the last W)

class B(A):
    def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
        A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
        self.__W=W

It seems dumb to write all this boiler-plate code, e.g passing all the args from B's Ctor to the inside call to A's ctor, since then every change to A's ctor must be applied to two other places in B's code.

I am guessing python has some idiom to handle such cases which I am unaware of. Can you point me in the right direction?

My best hunch, is to have a sort of Copy-Ctor for A and then change B's code into

class B(A):
     def __init__(self,instanceOfA,W):
         A.__copy_ctor__(self,instanceOfA)
         self.__W=W

This would suit my needs since I always create the subclass when given an instance of the father class, Though I am not sure whether it's possible...

A: 

A VERY hacky solution (of late, most of my ideas seem hacky... don't know why)

class A(object):
    def __init__(self):
        # do whatever
        self.newMethod()

    def newMethod(self):
        pass

class B(A):
    # don't redefine the constructor

    def newMethod(self, initValue=None):
        self.newValue = initValue
inspectorG4dget
I would have settled for this :) but where does `B.newMethod` get `initValue` from?
noam
With this solution, you'd have to call newMethod, giving you two phases for construction instead of just the one from \_\_init\_\_.
Roger Pate
A: 

Are you wanting something like this?

class A(object):
    def __init__(self, a, b, c, d, e, f, g):
        # do stuff
        print a, d, g

class B(A):
    def __init__(self, *args):
        args = list(args)
        self.__W = args.pop()
        A.__init__(self, *args)
Matt Anderson
well, this works for the very simplified version. But now B takes any number of args (less confining)...Isn't there something more idiomatic?
noam
B only kind of takes any number of arguments; it calls A and if A gets the wrong number it will raise `TypeError`. You could explicitly check the number of arguments if you wished. I probably would in production code.
Matt Anderson
If you find the wrong number of arguments, you would raise a TypeError yourself anyway.
Dinoboff
@Matt Anderson - yeah, but the first error is a clear error (one that for instance, pylint, would recognize before you ran your code), but the error your solution invokes is one that would only be found at run time
noam
P.S Another thing that I see as a disadvantage with this solution is that now B's __init__ docstring will have to be something like "this __init__ takes all of A's parameters, plus W as the last parameters" which is worse than having all the params written explicitly in the __init__...Well, maybe you just can't have the benefit of both worlds :)
noam
+1  A: 

Edit: based on Matt's suggestion, and to address gnibbler's concern re a positional-argument approach; you might check to make sure that the additional subclass-specific argument is being specified—similar to Alex's answer:

class B(A):
  def __init__(self, *args, **kwargs):
    try:
      self._w = kwargs.pop('w')
    except KeyError:
      pass
    super(B,self).__init__(*args, **kwargs)

>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3

Original answer:
Same idea as Matt's answer, using super() instead.

Use super() to call superclass's __init__() method, then continue initialising the subclass:

class A(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

class B(A):
  def __init__(self, w, *args):
    super(B,self).__init__(*args)
    self.w = w
Adam Bernier
This solution requires that w has to be passed as the first arg or by name. The question has it as the last arg
gnibbler
@gnu nibbler: thank you for your comment. I have edited my answer to address this concern; similar to what Alex posted many hours ago :-)
Adam Bernier
+4  A: 

Considering that arguments could be passed either by name or by position, I'd code:

class B(A):
    def __init__(self, *a, **k):
      if 'W' in k:
        w = k.pop('W')
      else:
        w = a.pop()
      A.__init__(self, *a, **k)
      self._W = w
Alex Martelli