HansSnah
HansSnah

Reputation: 2250

Python inheritance: pass all arguments from base to super class

I am not quite used to class inheritance in Python yet. All I want to do is simply pass all arguments from my base class to the super class when it is created:

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

    def do(self):
        c = self.a + self.b
        return B(c=c)

class B(A):
    def __init__(self, c):
        self.c = c

my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.c)

This works as expected. However, what I want is to also be able to call the arguments a and b from the x2 instance of the class my_B, so that I can directly write my_B.a for instance. I know this is done with super() like this:

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

    def do(self):
        c = self.a + self.b
        return B(a=self.a, b=self.b, c=c)

class B(A):
    def __init__(self, a, b, c):
        super(B, self).__init__(a=a, b=b)
        self.c = c

my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.a)
print(my_B.b)

However, I don't want to explicitly write all arguments of A when I create the instance of B. Is there a way to automatically pass all arguments from class A to class B?

Upvotes: 4

Views: 12335

Answers (3)

Francisco Vargas
Francisco Vargas

Reputation: 652

yup there is a way use key word arguments so :

class A(object):
    def __init__(self,**kwargs):
        # Non pythonic and a bit of a hack
        self.kwargs = kwargs
        vars(self).update(kwargs)

    def do(self):
        c = self.a + self.b
        return B(c=c, **kwargs)

class B(A):
    def __init__(self, c, **kwargs):
        self.c = c
        super(B, self).__init__(**kwargs)

my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.a)
print(my_B.b)
print(my_B.c)

This does what you are after nonethless the way in which it was written before was a bit more pythonic when run this should output:

1
2
3

The downside of doing this is that now A has not limit in terms of the number of attributes but you could ensure this with an assertion or something I guess.

Upvotes: 2

HansSnah
HansSnah

Reputation: 2250

Ok, thanks for your comments. I have come up with this solution:

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

    def do(self):
        c = self.a + self.b
        return B(self, c)

class B:
    def __init__(self, base, c):
        self.base = base
        self.c = c

my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.base.a)
print(my_B.base.b)
print(my_B.c)

This removes the inheritance of class B and makes the code slightly less readable, but I guess it will do, right? 😊

Upvotes: 2

Claudiu
Claudiu

Reputation: 229361

Based on your comment, you could do something like this:

class B(A):
    def __init__(self, c, an_a): 
        super(B, self).__init__(an_a.a, an_a.b)
        self.c = c

You may instead prefer to keep your current constructor and add a from_a static method:

class B(A):
    def __init__(self, c, a, b):  # note order
        super(B, self).__init__(a=a, b=b)
        self.c = c

    @staticmethod
    def from_a(c, an_a):
        return B(c, an_a.a, an_a.b)

Finally, if you don't want to type out all of those parameters, you can add an args() method to A and then use the collection unpacking function syntax:

class A:
    ...
    def args(self):
        return (self.a, self.b)

class B(A):
    def __init__(self, c, *args):        # Note *args
        super(B, self).__init__(*args)
        self.c = c

    @staticmethod
    def from_a(c, an_a):
        return B(c, *an_a.args())

Now B's constructor takes the parameter special to B, followed by any number of parameters which just get passed to A's constructor. This allows you to do the tuple unpacking when calling the constructor, instead of listing everything out manually.

Upvotes: 4

Related Questions