1

Are there any "good" way to manage both args and kwargs in a inheritance hierarchy like I tried in this code. I mean without to get a value with a specified key in kwargs or something like this...

It is supposed to display 1 2 3 4 :

class Parent(object):
    def __init__(self, motherArg1, motherArg2=100):
        self.motherArg1 = motherArg1
        self.motherArg2 = motherArg2
    def printParent(self):
        print self.motherArg1
        print self.motherArg2

class Child(Parent):
    def __init__(self, childArg1, *args, childArg2=100, **kwargs): # Doesn't work here
        super(Child, self).__init__(*args, **kwargs)
        self.childArg1 = childArg1
        self.childArg2 = childArg2
    def printChild(self):
        print self.childArg1
        print self.childArg2

child = Child(1, 3, childArg2=2, motherArg2=4)
child.printChild()
child.printParent()

The syntax is not good : expected ");" after *args.

And def __init__(self, childArg1, childArg2=100, *args, **kwargs) is a right syntax but doesn't work.

  1. When I try this syntax and child = Child(1, childArg2=2, 3, motherArg2=4), I get SyntaxError: non-keyword arg after keyword arg
  2. And when I try child = Child(1, 3, childArg2=2, motherArg2=4) I get TypeError: __init__() got multiple values for keyword argument 'childArg2'
hayj
  • 1,159
  • 13
  • 21
  • 1
    Please [edit] to include that, properly formatted, *in the question itself*. In this case it's pretty obvious what output you would get but a [mcve] is a good habit to get into. – jonrsharpe Jun 15 '16 at 09:02

2 Answers2

3

In Python 2, you must put the *args argument after any explicit keyword arguments:

def __init__(self, childArg1, childArg2=100, *args, **kwargs):

However, you can't then use additional positional parameters anyway as they are captured by the childArg2 argument:

>>> child = Child(1, 3, childArg2=2, motherArg2=4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got multiple values for keyword argument 'childArg2'

Your only option is to take keyword arguments from the **kwargs dictionary:

def __init__(self, childArg1, *args, **kwargs):
    childArg2 = kwargs.pop('childArg2', 2)

This makes childArg2 work only as an explicit keyword argument, and *args captures all additional positional arguments:

>>> class Parent(object):
...     def __init__(self, motherArg1, motherArg2=100):
...         self.motherArg1 = motherArg1
...         self.motherArg2 = motherArg2
...     def printParent(self):
...         print self.motherArg1
...         print self.motherArg2
...
>>> class Child(Parent):
...     def __init__(self, childArg1, *args, **kwargs):
...         childArg2 = kwargs.pop('childArg2', 2)
...         super(Child, self).__init__(*args, **kwargs)
...         self.childArg1 = childArg1
...         self.childArg2 = childArg2
...     def printChild(self):
...         print self.childArg1
...         print self.childArg2
...
>>> child = Child(1, 3, childArg2=2, motherArg2=4)
>>> child.printChild()
1
2
>>> child.printParent()
3
4
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

when you rename printChild of the parent to printParent (and fix the prints) it already works in python 3, as suggested:

1
2
3
4

But you can get it to work for python2, too. You'd do this by removing entries in kwargs which are supposed to be relevant for the child before you pass them on to the parent.

Code (working for python3):

class Parent(object):
    def __init__(self, motherArg1, motherArg2=100):
        self.motherArg1 = motherArg1
        self.motherArg2 = motherArg2
    def printParent(self):
        print(self.motherArg1)
        print(self.motherArg2)

class Child(Parent):
    def __init__(self, childArg1, *args, childArg2=100, **kwargs):
        super(Child, self).__init__(*args, **kwargs)
        self.childArg1 = childArg1
        self.childArg2 = childArg2
    def printChild(self):
        print(self.childArg1)
        print(self.childArg2)

child = Child(1, 3, childArg2=2, motherArg2=4)
child.printChild()
child.printParent()

Code for python2

class Parent(object):
    def __init__(self, motherArg1, motherArg2=100):
        self.motherArg1 = motherArg1
        self.motherArg2 = motherArg2
    def printParent(self):
        print(self.motherArg1)
        print(self.motherArg2)

class Child(Parent):
    def __init__(self, childArg1, *args, **kwargs): 
        # this shows the concept, it can be formulated more elegantly
        # with @Martijn Pieters answer's 'pop':
        if 'childArg2' in kwargs:
            childArg2 = kwargs['childArg2']
            del kwargs['childArg2']
        else:
            childArg2 = 2
        super(Child, self).__init__(*args, **kwargs)
        self.childArg1 = childArg1
        self.childArg2 = childArg2
    def printChild(self):
        print(self.childArg1)
        print(self.childArg2)

child = Child(1, 3, childArg2=2, motherArg2=4)
child.printChild()
child.printParent()
DomTomCat
  • 8,189
  • 1
  • 49
  • 64
  • 1
    Since the OP is using explicit `object` and the `print` statement, not to mention is reporting that their code *doesn't* work, it's pretty clear that they are using 2.x. Telling them it works in 3.x isn't terribly useful. – jonrsharpe Jun 15 '16 at 09:03
  • you're right. I've now added a python 2 version – DomTomCat Jun 15 '16 at 09:04
  • No you said it works on Python 3, so maybe I prefer change my python version. Thank you! – hayj Jun 15 '16 at 09:12