'How to minimize redundancy in passing kwargs to multiple super classes?
Given the following classes A, B, and C:
class A:
def __init__(self, a, aa, aaa):
self.a = a
self.aa = aa
self.aaa = aaa
class B:
def __init__(self, b, bb, bbb):
self.b = b
self.bb = bb
self.bbb = bbb
class C(A, B):
def __init__(self, **kwargs):
super(C, self).__init__(**kwargs)
I want to avoid having to repeat all the superclasses parameters a, aa, aaa, b, bb, bbb, in C definition:
class C(A, B):
def __init__(self, a, aa, aaa, b, bb, bbb):
super(C, self).__init__(**kwargs)
and somehow pass A and B kwargs to be resolved in super().__init__ call but this is not possible using the way I described and will result in an error:
>>> c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)
TypeError: A.__init__() got an unexpected keyword argument 'b'
The correct way of doing so is calling A.__init__(self, **a_kwargs) and B.__init__(self, **b_kwargs) but as I said this creates redundant parameters I'm trying to avoid. Is there a better way to achieve the same thing?
Solution 1:[1]
You can do this:
class A:
def __init__(self, a, aa, aaa, **kwargs):
self.a = a
self.aa = aa
self.aaa = aaa
super().__init__(**kwargs)
class B:
def __init__(self, b, bb, bbb, **kwargs):
self.b = b
self.bb = bb
self.bbb = bbb
super().__init__(**kwargs)
class C(A, B):
pass
c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)
Since the C's MRO is [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>], A will consume it's parameters and pass the rest to B.
- If you need to pass positional arguments too, You can change it to:
class A:
def __init__(self, a, aa, aaa, *args, **kwargs):
self.a = a
self.aa = aa
self.aaa = aaa
super().__init__(*args, **kwargs)
class B:
def __init__(self, b, bb, bbb, *args, **kwargs):
self.b = b
self.bb = bb
self.bbb = bbb
super().__init__(*args, **kwargs)
class C(A, B):
pass
Solution 2:[2]
You could use built-in signature(callable) function to obtain expected keyword arguments for each __init__ and pass only those that the function expects like so. Whether this is good design, that is a matter of another discussion.
from inspect import signature
class A:
def __init__(self, a, aa, aaa):
self.a = a
self.aa = aa
self.aaa = aaa
print(f"A obj: {a=}, {aa=}, {aaa=}")
class B:
def __init__(self, b, bb, bbb):
self.b = b
self.bb = bb
self.bbb = bbb
print(f"B obj: {b=}, {bb=}, {bbb=}")
class C(A, B):
def __init__(self, **kwargs):
A_params = {k:v for k, v in kwargs.items() if k in signature(A.__init__).parameters.keys()}
A.__init__(self, **A_params)
B_params = {k:v for k, v in kwargs.items() if k in signature(B.__init__).parameters.keys()}
B.__init__(self, **B_params)
c = C(a=1, aa=2, aaa=3, b=4, bb=5, bbb=6)
Output:
A obj: a=1, aa=2, aaa=3
B obj: b=4, bb=5, bbb=6
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | matszwecja |
