0

In the case that you want to extend a class and its respective __init__ method, is there a way to handle a large number of arguments including new ones? For example:

class Thing:
    def __init__(self, arg1, arg2, arg3, arg4):
        # Initialize 

class SubThing(Thing):
    def __init__(self, arg1, arg2, arg3, arg4, ex_arg1, ex_arg2):
        super().__init__(arg1, arg2, arg3, arg4):
        # Do extra initialization 

Is it possible to shorten the list of arguments for the __init__ method of the subclass? Answers such as this one don't describe how to add arguments to the init, or how to handle extra keyword arguments such as __init__(self, arg1, arg_room='Bedroom', ex_arg1, ex_position=(0, 0), ex_arg2).

Community
  • 1
  • 1
Tankobot
  • 1,492
  • 14
  • 21
  • 1
    Short answer: No, there is no way to do this without writing out all the arguments you want to pass. – poke Mar 05 '16 at 22:27
  • there is *args, and **kwargs but I don't think that's exactly what you are asking for. – Morgan G Mar 05 '16 at 22:29

2 Answers2

0

There isn't really a great way to do what you want. Generally speaking, naming all the arguments is the main way to go.

There are a few approaches that might help though. One is bundling up some of the arguments into larger collections, so you only need to pass one item on to the parent class, rather than several. Here's what that might look like using an iterable sequence to hold arg1-arg4, but you might want to use a custom class instead (see the Dialect class in the csv module for an example of a type that bundles up several arguments for other types):

def __init__(self, thing_args, ex_arg1, ex_arg2):
    super().__init__(*thing_args)
    # ...

The other way to go is to use *args and/or **kwargs to receive the arguments you want to pass along. Using positional *args is rather limiting, since you have to list the arguments you care about in the subclass ahead of all the arguments you're passing along:

def __init__(self, ex_arg1, ex_arg2, *args):
    super().__init__(*args)
    # ...

Callers will need to list the values for the ex_argsN paramters first, which may be unnatural. It gets ever worse the more subclasses there are.

Requiring everything be passed as keyword arguments may be nicer, since the caller can put the keyword arguments in whatever order seems most natural:

def __init__(self, *, ex_args1, ex_args2, **kwargs):
    super.__init__(self, **kwargs) # passes along the unnamed keyword args
    # ...

The biggest downside to this approach is that a call gets rather verbose with all the parameter names:

st = SubThing(arg1="foo", arg2="bar", arg3="baz", arg4="quux",
              ex_arg1=1, ex_arg2=6.02e23)
Blckknght
  • 100,903
  • 11
  • 120
  • 169
0

You can do something similar to the referenced answer except remove the items that are specific to your subclass.

class Thing:
    def __init__(self, arg1, arg2, arg3, arg4, kw1=None, kw2=None):
        print('Thing args:',arg1,arg2,arg3,arg4)
        print('Thing kw:',kw1,kw2)

class SubThing(Thing):
    def __init__(self, *args, **kw):
        """SubThing(arg1, arg2, arg3, arg4, arg5, arg6, kw1=None, kw2=None,
        kw3=None, kw4=None)
        """
        if len(args) != 6:
            raise TypeError(
                "SubThing() takes 6 positional arguments but {} were given"
                .format(len(args)))
        arg5, arg6 = args[4:]
        kw3 = kw.pop('kw3', None)
        kw4 = kw.pop('kw4', None)
        super().__init__(*args[:4], **kw)


SubThing(1,2,3,4,5,6,kw1='kw1',kw4='kw4')
print('---- but populating kwargs from positional args stops working ----')
Thing(1,2,3,4, 'kw1', 'kw2')
SubThing(1,2,3,4,5,6,'kw1','kw2')

This results in

Thing args: 1 2 3 4
Thing kw: kw1 None
---- but populating kwargs from positional args stops working ----
Thing args: 1 2 3 4
Thing kw: kw1 kw2
Traceback (most recent call last):
  File "n.py", line 24, in <module>
    SubThing(1,2,3,4,5,6,'kw1','kw2')
  File "n.py", line 14, in __init__
    .format(len(args)))
TypeError: SubThing() takes 6 positional arguments but 8 were given

Notice in the second example, you lost some functionality because extra positional arguments didn't automatically fill in keyword arguments. You can fix that with more coding or just accept the restrictions. Lots of classes that use *args, **kw forget to do that so you are in good company.

tdelaney
  • 73,364
  • 6
  • 83
  • 116