class A:
def __init__(self, x1, x2, x3, x4):
pass
...
class B(A):
def __init__(self, x1, x2, x3, x4, y1, y2):
super().__init__(x1, x2, x3, x4) # How to replace this without repeating the argument names?
...
I prefer not to use **kwargs or dicts, as it hides the actual signature.
One solution could be something like:
from inspect import signature
class B(A):
def __init__(self, x1, x2, x3, x4, y1, y2):
sig = signature(super().__init__)
params = {k: v for k, v in locals().items() if k in sig.parameters}
super().__init__(**params)
...
But this is cumbersome and has its own problems. Is there anything more elegant that could be done?
Edit: The motivation to this question is the DRY principle.
I understand that usually this question is to be rather avoided - better use a dataclass as per Mario's answer, or just write the arguments again as suggested in the comments.
But for the sport and the chance that this will actually be useful to someone someday, I came up with the following solution:
import inspect
from functools import wraps
def calling_base_init(init):
@wraps(init)
def wrapper(*args, **kwargs):
sig_init = inspect.signature(init)
bargs = sig_init.bind(*args, **kwargs)
for cls in args[0].__class__.__bases__:
sig = inspect.signature(cls.__init__)
params = {k: v for k, v in bargs.arguments.items() if k in sig.parameters if k != 'self'}
if len(params) == len(sig.parameters) - 1:
cls.__init__(args[0], **params)
break
else:
assert False, f"No appropriate signature was found among base classes of {args[0].__class__.__name__}"
init(*args, **kwargs)
return wrapper
And then use the decorator on the derived class's __init__:
class A:
def __init__(self, x1, x2, x3, x4):
print(x1, x2, x3, x4)
class B(A):
@calling_base_init
def __init__(self, x1, x2, x3, x4, y1, y2):
print(y1, y2)
B(11, 12, 13, 14, 21, 22)
Outputs:
11 12 13 14
21 22
as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With